From f8f85394850b5b1254205833fae8912b2062c362 Mon Sep 17 00:00:00 2001 From: IAmNotHanni Date: Sun, 12 Apr 2020 17:47:00 +0200 Subject: [PATCH] Get ready for first alpha demo. https://github.com/inexorgame/vulkan-renderer/issues/52. --- assets/models/inexor/inexor_2.gltf | 110 +++ src/inexor_application.cpp | 1204 ++++++++++++++-------------- src/vulkan-renderer/renderer.cpp | 2 +- 3 files changed, 714 insertions(+), 602 deletions(-) create mode 100644 assets/models/inexor/inexor_2.gltf diff --git a/assets/models/inexor/inexor_2.gltf b/assets/models/inexor/inexor_2.gltf new file mode 100644 index 000000000..a9939bdbd --- /dev/null +++ b/assets/models/inexor/inexor_2.gltf @@ -0,0 +1,110 @@ +{ + "asset" : { + "generator" : "Khronos glTF Blender I/O v1.1.45", + "version" : "2.0" + }, + "scene" : 0, + "scenes" : [ + { + "name" : "Scene", + "nodes" : [ + 0 + ] + } + ], + "nodes" : [ + { + "mesh" : 0, + "name" : "Cube", + "scale" : [ + 2.890000104904175, + 0.1899999976158142, + 7.839360237121582 + ], + "translation" : [ + 0, + 0, + -5 + ] + } + ], + "meshes" : [ + { + "name" : "Cube.002", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3 + } + ] + } + ], + "accessors" : [ + { + "bufferView" : 0, + "componentType" : 5126, + "count" : 1000, + "max" : [ + 1, + 1, + 1 + ], + "min" : [ + -1, + -1.0000003576278687, + -1 + ], + "type" : "VEC3" + }, + { + "bufferView" : 1, + "componentType" : 5126, + "count" : 1000, + "type" : "VEC3" + }, + { + "bufferView" : 2, + "componentType" : 5126, + "count" : 1000, + "type" : "VEC2" + }, + { + "bufferView" : 3, + "componentType" : 5123, + "count" : 2868, + "type" : "SCALAR" + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteLength" : 12000, + "byteOffset" : 0 + }, + { + "buffer" : 0, + "byteLength" : 12000, + "byteOffset" : 12000 + }, + { + "buffer" : 0, + "byteLength" : 8000, + "byteOffset" : 24000 + }, + { + "buffer" : 0, + "byteLength" : 5736, + "byteOffset" : 32000 + } + ], + "buffers" : [ + { + "byteLength" : 37736, + "uri" : "data:application/octet-stream;base64,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Kc/eMwAAgL9GnqSxKc/eMwAAgL9GnqSxKc/eMwAAgL9GnqSxKc/eMwAAgL9GnqSxAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAKuhsgAAgL8AAACAAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8uXKowAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAHpD0NAAAgL8ermO0HpD0NAAAgL8ermO0HpD0NAAAgL8ermO0HpD0NAAAgL8ermO0HpD0NAAAgL8ermO0HpD0NAAAgL8ermO0HpD0NAAAgL8ermO0HpD0NAAAgL8ermO0HpD0NAAAgL8ermO0HpD0NAAAgL8ermO0HpD0NAAAgL8ermO0HpD0NAAAgL8ermO0HpD0NAAAgL8ermO0AAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAGaKntP//f78w+to0GaKntP//f78w+to0GaKntP//f78w+to0GaKntP//f78w+to0GaKntP//f78w+to0GaKntP//f78w+to0GaKntP//f78w+to0GaKntP//f78w+to0GaKntP//f78w+to0GaKntP//f78w+to0nKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAnKNtNAAAgL8AAACAFWEKvWehM79sLzY/FWEKvWehMz9sLzY/HBcOP19jLz/jgfE+HBcOP19jL7/jgfE+QCEgPmr1ND9hlzA/QCEgPmr1NL9hlzA/QbkgPmrpNL9hmTA/QbkgPmrpND9hmTA/N70bv1wdLj+jZdG+N70bv1wdLr+jZdG+RYEiPmrhNL9hhzA/RYEiPmrjND9hhzA/KM0TvyjLE78oyxM/agM1v2oDNb8AAACATCEmvzFtGL/lffK+rCnWvp1nTj+sJdY+TCEmvzFtGD/lffK+Ni0bP1wZLj+mGdM+Ni0bP1wZLr+mGdM+/s3+Pl+5Lz8Puwc//s3+Pl+5L78Puwc/DWEGvWjJMz9sCza/DWEGvWjJM79sCza/AAEAOGetM79tVzY/AAEAOGetMz9tVzY/R3kjPmrlND9hdzA/R3kjPmrlNL9hdzA/KM0TvyjNE78ozRM/KM0TvyjNEz8ozRM/BCGCvmV7Mj9Xlys/BCGCvmV7Mr9Xlys/KM0TPyjNEz8ozRM/KM0TPyjNE78ozRM/cAE4P2OhMb9gQTC9cAE4P2OhMT9gQTC9S4MlP12JLj9eNa8+S4MlP12JLr9eNa8+AAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAP//fz8AAACAAAAAAP//fz8AAACAAAAAAP//fz8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACA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TQBNAE8AUABQAE8AUQBHAE0AUgBSAE0AUwBTAE0AUABUAFMAVQBVAFMAVgBWAFMAUABVAFYAVwBXAFYAWABZAFAAUQBUAFUAWgBbAFcAWABcAF0AXgBcAF4AXwBgAGEAYgBgAGIAYwBkAGUAZgBkAGYAZwBoAGkAagBoAGoAawBsAG0AbgBsAG4AbwBwAHEAcgBwAHIAcwB0AHUAdgB0AHYAdwB4AHkAegB4AHoAewB8AH0AfgB8AH4AfwCAAIEAggCAAIIAgwCEAIUAhgCEAIYAhwCIAIkAigCIAIoAiwCMAI0AjgCMAI4AjwCQAJEAkgCQAJIAkwCUAJUAlgCUAJYAlwCYAJkAmgCYAJoAmwCcAJ0AngCcAJ4AnwCgAKEAogCgAKIAowCkAKUApgCkAKYApwCoAKkAqgCoAKoAqwCsAK0ArgCsAK4ArwCwALEAsgCwALIAswC0ALUAtgC0ALYAtwCLAIoAuACLALgAuQC6ALsAvAC6ALwAvQC+AL8AwAC+AMAAwQDCAMMAxADCAMQAxQDGAMcAyADGAMgAyQDKAMYAyQDLAMwAzQDLAM0AzgDPANAA0QDPANEA0gDTANQA1QDTANUA1gDXANgA2QDXANkA2gDbANwA3QDbAN0A3gDfAOAA4QDfAOEA4gDjAOQAsQDjALEAsADlAOYAuwDlALsAugDnAOgA6QDnAOkA6gDnAOoA6wDsAOcA6wDtAO4A7wDtAO8A8ADxAPIA8wDxAPMA9AD1APYA9wD1APcA+AD5APoA2AD5ANgA1wD7APwA/QD7AP0A/gDeAN0A/wDeAP8AAAEBAQIBAwEBAQMBBAGbAJoABQGbAAUBBgEHAQgBCQEHAQkBCgELAQwBDQELAQ0BDgEPARABnQAPAZ0AnAARARIBEwERARMBFAEVARYBFwEVARcBGAEZARoBGwEZARsBHAEUARMBHQEUAR0BHgEfASABIQEfASEBIgHJAMgAIwHJACMBJAElASYBJwElAScBKAEpASoBKwEpASsBLAEtAS4BLwEtAS8BMAF3AHYAMQF3ADEBMgEzATQBNQEzATUBNgE3ATgBOQE3ATkBOgHSANEAdQDSAHUAdAA7ATwBPQE7AT0BPgE/AUABQQE/AUEBQgFDAUQBkQBDAZEAkABFAUYBRwFFAUcBSAFJAUoBhQBJAYUAhABjAGIASwFjAEsBTAG9ALwATQG9AE0BTgFPAVABKgFPASoBKQFMAUsBUQFMAVEBUgFTAVQBcQBTAXEAcABVAVYBaQBVAWkAaABXAVgBWQFXAVkBWgHOAM0AEAHOABABDwFbAVwBXQFbAV0BXgFfAWABIAFfASABHwFSAVEBYQFSAWEBYgFjAWQBvwBjAb8AvgAAAf8AZQEAAWUBZgFnAWgBqQBnAakAqADBAMAAaQHBAGkBagGrAKoAawGrAGsBQAGrAEABPwFsAasAPwGHAIYARgGHAEYBRQEkASMBbQEkAW0BbgEcARsBbwEcAW8BcAFxAXIBcwFxAXMBdAF1AXYBdwF1AXcBeAGvAK4AYQCvAGEAYAB5AXoBewF5AXsBfAEoAScB6AAoAegA5wB4AXcBfQF4AX0BfgHaANkAfwHaAH8BgAGBAYIBgwGBAYMBhAGFAYYBhwGFAYcBiAGJAYoBiwGJAYsBjAFOAU0BYAFOAWABXwGNAY4BbQCNAW0AbACPAZABggGPAYIBgQGRAZIB5ACRAeQA4wCjAKIAjgGjAI4BjQGTAZQBdgGTAXYBdQE2ATUBOAE2ATgBNwGVAZYBZAGVAWQBYwGXAZgBgQCXAYEAgACZAZoBVgGZAVYBVQFeAV0BmwFeAZsBnAEiASEBnQEiAZ0BngGfAaABoQGfAaEBogGzALIAowGzAKMBpAGlAaYBpwGlAacBqAGpAaoBqwGsAakBqwGsAasBrQGuAawBrQGvAbABpgGvAaYBpQGfAJ4AsQGfALEBsgHrAOoAswHrALMBtAFaAVkB8gBaAfIA8QC1AbYBtwG1AbcBuAG5ALgA1AC5ANQA0wC5AboBuwG5AbsBvAG9Ab4BvwG9Ab8BwAFiAWEBwQFiAcEBwgHDAcQBxQHDAcUBxgGeAZ0BxwGeAccByAEGAQUByQEGAckBygHLAcwBsAHLAbABrwHNAc4BxAHNAcQBwwEYARcB3AAYAdwA2wD+AP0AvgH+AL4BvQFzAHIAFgFzABYBFQFIAUcBzwFIAc8B0AHRAdIBEgHRARIBEQHTAdQBzgHTAc4BzQHVAdYB1wHVAdcB2AFnAGYA2QFnANkB2gFCAUEBNAFCATQBMwHbAdwBqQHbAakBrAG4AbcB3QG4Ad0B3gG8AbsBcgG8AXIBcQFwAW8BZQBwAWUAZADfAeABXAHfAVwBWwGoAacB4QGoAeEB4gHGAcUBfQDGAX0AfACyAbEB4wGyAeMB5AF+AX0BoQB+AaEAoAAOAQ0B5QEOAeUB5gHQAc8BaAHQAWgBZwHnAegB6QHnAekB6gHFAMQA6wHFAOsB7AHIAccB/ADIAfwA+wCXAJYA4AGXAOAB3wHiAeEB7QHiAe0B7gHvAfABegHvAXoBeQHxAfIBmAHxAZgBlwHzAfQBAgHzAQIBAQH1AfYBoAH1AaABnwHiAOEA9wHiAPcB+AH5AfoBXQD5AV0AXAB8AXsB+wF8AfsB/AHeAd0B1gHeAdYB1QH9Af4BLgH9AS4BLQFqAWkB9gFqAfYB9QGMAYsB/wGMAf8BAALaAdkBAQLaAQECAgIDAgQCBQIDAgUCBgKnAKYASgGnAEoBSQEHAggCCQIHAgkCCgLuAe0B3AHuAdwB2wFmAWUB+gFmAfoB+QEwAS8BCwIwAQsCDAJuAW0BJgFuASYBJQHqAekBiQDqAYkAiAA+AT0B7gA+Ae4A7QDwAO8ADQLwAA0CDgIKAgkCkAEKApABjwGPAI4A8gGPAPIB8QHsAesBWAHsAVgBVwE6ATkBDwI6AQ8CEAICAgECEQICAhECEgITAhQCtQATArUAtAAQAg8CwwAQAsMAwgCDAIIAmQCDAJkAmACTAJIA9gCTAPYA9QBfAF4AxwBfAMcAxgAVAhYCFwIXAhYCGAIZAhUCFwIaAhkCFwIbAhoCFwIcAhsCFwIdAhwCFwIeAh0CFwIfAh4CFwIgAh8CFwIhAiACFwIiAiECFwIjAiICFwIkAiMCFwIlAiQCFwImAhcCGAInAigCKQInAikCKgIqAikCKwIrAikCLAIsAikCLQItAikCLgIuAikCLwIvAikCMAIwAikCMQIxAikCMgIyAikCMwIzAikCJgIzAiYCNAI0AiYCNQI1AiYCNgI2AiYCNwI3AiYCOAI4AiYCOQI5AiYCOgI6AiYCOwI7AiYCPAI8AiYCPQI9AiYCPgI+AiYCPwI/AiYCQAJAAiYCGAInAioCQQJCAkACGAJDAkQCRQJGAkMCRQJGAkUCRwJIAkYCRwJIAkcCSQJKAkgCSQJKAkkCSwJMAkoCSwJMAksCTQJOAkwCTQJOAk0CTwJQAk4CTwJQAk8CUQJSAlACUQJSAlECUwJUAlICUwJUAlMCVQJWAlQCVQJWAlUCVwJYAlYCVwJYAlcCWQJaAlgCWQJaAlkCWwJcAloCWwJcAlsCXQJeAlwCXQJeAl0CXwJgAl4CXwJgAl8CYQJiAmACYQJiAmECYwJkAmICYwJkAmMCZQJmAmQCZQJmAmUCZwJoAmYCZwJoAmcCaQJqAmgCaQJqAmkCawJsAmoCawJsAmsCbQJuAmwCbQJuAm0CbwJwAm4CbwJwAm8CcQJyAnACcQJzAnQCdQJ2AncCeAJzAnUCeQJ6AnYCeAJzAnkCewJ8AnoCfQJ9AnoCeAJzAnsCfgJ/AnwCfQJzAn4CgAKBAn8CfQJzAoACggKCAoACgwKEAoECfQKCAoMChQKGAoQCfQKCAoUChwKIAoYCfQKCAocCiQKKAogCfQKCAokCiwKMAooCfQKCAosCjQKOAowCfQKCAo0CjwKQAo4CfQKCAo8CkQKRApACfQKCApECfQKCAn0CkgJzAoICkwJzApMClAKUApMClQKWApcCmAKZApYCmgKaApYCmAKbApwCnQKeAp8CmwKdApoCmAKeApsCoAKgApsCnQKgAp0CoQKhAp0CogKiAp0CmAKgAqECowKjAqECpAKkAqECpQKmAqMCpwKnAqMCpAKmAqcCqAKpAqQCpQKqAqsCrAKtAq4CrwKtAq8CsAKwAq8CqgKwAqoCsQKxAqoCrAKtArACsgKzArECrAKtArICtAK0ArICtQKtArQCtgKeArcCuAK4ArcCuQK4ArkCugK6ArkCuwK8Ar0CvgK/ArwCwALAArwCvgLBAsACvgLCAsECvgLDAsQCxQLDAsUCwgLDAsICvgLGAscCyALGAsgCyQLKAsYCyQLKAskCywLMAsoCywLMAssCzQLOAswCzQLOAs0CzwLQAs4CzwLQAs8C0QLSAtAC0QLSAtEC0wLUAtIC0wLUAtMC1QLWAtQC1QLWAtUC1wLYAtYC1wLYAtcC2QLaAtgC2QLaAtkC2wLcAtoC2wLcAtsC3QLeAtwC3QLfAt4C3QLfAt0C4AKEAYMBGgGEARoBGQF0AXMB4QJ0AeEC4gLjAuQCPAHjAjwBOwHlAuYC5wLlAucC6ALpAuoC6AHpAugB5wGcAZsB6wKcAesC7AL4AfcB1AH4AdQB0wEGAgUClQAGApUAlADKAckBzADKAcwAywDtAu4C7wLwAu0C7wLwAu8C8QKAAX8BBAKAAQQCAwIOAg0C5gAOAuYA5QDyAvMCFALyAhQCEwL0AvUC8wL0AvMC8gL2AvcCCAH2AggBBwEyATEBVAEyAVQBUwHAAb8B+gDAAfoA+QDiAuEC+ALiAvgC+QIKAQkBUAEKAVABTwFvAG4A9AFvAPQB8wH6AvsCtgH6ArYBtQEMAgsCjQAMAo0AjAAeAR0B/AIeAfwC/QIEAQMBpQAEAaUApADoAucC+wLoAvsC+gKIAYcB0gGIAdIB0QHsAusC5gLsAuYC5QKkAaMBrQCkAa0ArAD+Av8CugH+AroBuQEAAwEDAgMAAwIDAwPkAeMB6gLkAeoC6QIEAwUD5AIEA+QC4wL5AvgClgH5ApYBlQH8AfsB9wL8AfcC9gL9AvwCAQP9AgEDAAN7AHoA8AF7APAB7wEAAv8BeQAAAnkAeADmAeUBzAHmAcwBywF/AH4A7QJ/AO0C8AIGAwcDCAMIAwkDCgMKAwkDCwMGAwgDDAMMAwgDCgMNAwoDCwMOAw0DDwMPAw0DCwMQAw8DCwMGAwwDEQMGAxEDEgMSAxEDEwMGAxIDFAMGAxQDFQMVAxQDEAMVAxADFgMWAxADCwMXAxYDCwNHABgDGQNHABkDGgNHABoDUQAbAxwDHQMeAx8DCwMgAx4DCwMbAx0DIQMiAyADCwMbAyEDIwMkAyIDCwMbAyMDJQMmAyQDCwMbAyUDJwMoAyYDKQMpAyYDCwMbAycDKgMrAygDKQMbAyoDLAMtAysDKQMbAywDLgMvAy0DKQMbAy4DMAMxAy8DKQMbAzADMgMzAzEDKQMbAzIDNAM1AzMDKQMbAzQDNgM3AzUDKQMbAzYDNwMbAzcDKQM4AxsDKQM5AykDCwM6AzkDOwM7AzkDCwM8AzsDCwMdAxwDPQM9AxwDHwM9Ax8DPgM+Ax8DPwM/Ax8DQANAAx8DQQNBAx8DQgNCAx8DQwNDAx8DRANEAx8DRQNFAx8DRgNGAx8DRwNHAx8DSANIAx8DHgNJAz0DPgNKA0kDPgNLA0oDPgNMA0sDPgNNA0wDTgNOA0wDPgNPA00DTgNQA08DTgNRA1ADTgNSA1EDTgNTA1IDTgNUA1MDTgNVA1QDTgNWA1UDTgNXA1YDWANYA1YDTgNZA1cDWANaA1kDWANaA1gDWwNaA1sDXANaA1wDXQNaA10DXgNfA1oDXgNfA14DYANfA2ADYQNiA18DYQNiA2EDYwNkA2IDYwNkA2MDZQNmA2QDZQNmA2UDZwNoA2YDZwNoA2cDaQNqA2gDaQNqA2kDawNsA2oDawNsA2sDbQNuA0gDHgNvA24DHgMdAz0DcAMdA3ADcQNxA3ADcgNxA3IDcwNxA3MDdANxA3QDdQNxA3UDdgNxA3YDdwNxA3cDeANxA3gDeQNxA3kDegNxA3oDewNxA3sDfANxA3wDfQNxA30DfgNxA34DfwNxA38DgAMdA3EDgQMdA4EDggOCA4EDgwODA4EDhAOEA4EDhQOFA4EDhgOGA4EDhwOHA4EDiAOIA4EDiQOJA4EDigOKA4EDiwOLA4EDbwOLA28DjAOMA28DjQONA28DjgOOA28DjwOPA28DkAOQA28DkQORA28DkgOSA28DkwOTA28DlAOUA28DlQOVA28DlgOWA28DlwOXA28DmAOYA28DHgOZA5gDHgMdA4IDmgObA5wDnQOeA5sDnQOeA50DnwOgA54DnwOgA58DoQOiA6ADoQOiA6EDowOkA6IDowOkA6MDpQOmA6QDpQOmA6UDpwOoA6YDpwOoA6cDqQOqA6gDqQOqA6kDqwOsA6oDqwOsA6sDrQOuA6wDrQOuA60DrwOwA64DrwOwA68DsQOyA7ADsQOyA7EDswO0A7IDswO0A7MDtQO2A7QDtQO2A7UDtwO4A7YDtwO4A7cDuQO6A7gDuQO6A7kDuwO8A7oDuwO8A7sDvQO+A7wDvQO+A70DvwPAA74DvwPAA78DwQPCA8ADwQPCA8EDwwPEA8IDwwPEA8MDxQPGA8QDxQPGA8UDxwPIA8YDxwPIA8cDyQPKA8gDyQPLA8wDzQPOA8sDzQPOA80DzwPQA84DzwPQA88D0QPSA9AD0QPSA9ED0wPUA9ID0wPUA9MD1QPWA9QD1QPWA9UD1wPYA9YD1wPYA9cD2QPaA9gD2QPaA9kD2wPcA9oD2wPcA9sD3QPeA9wD3QPeA90D3wPgA94D3wPgA98D4QPiA+AD4QPiA+ED4wPkA+ID4wPkA+MD5QPWANUA/wLWAP8C/gJrAGoAigFrAIoBiQHmA+cDBQPmAwUDBAPYAdcB0ADYAdAAzwCiAaEB9QKiAfUC9AIsASsBhgEsAYYBhQH0APMADAH0AAwBCwE=" + } + ] +} diff --git a/src/inexor_application.cpp b/src/inexor_application.cpp index ae839b9a0..87efedcb8 100644 --- a/src/inexor_application.cpp +++ b/src/inexor_application.cpp @@ -1,812 +1,814 @@ -#include "inexor_application.hpp" +#include "inexor_application.hpp" -namespace inexor { -namespace vulkan_renderer { - - - /// @brief Static callback for window resize events. - /// @note Because GLFW is a C-style API, we can't pass a poiner to a class method, so we have to do it this way! - /// @param window The GLFW window. - /// @param height The width of the window. - /// @param height The height of the window. - /// @TODO Avoid static methods! Poll the events manually in the render loop! - static void frame_buffer_resize_callback(GLFWwindow* window, int width, int height) - { - spdlog::debug("Frame buffer resize callback called. window width: {}, height: {}", width, height); - - // This is actually the way it is handled by the official Vulkan samples. - auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); - app->frame_buffer_resized = true; - } - - - /// @brief Static callback for window resize events. - /// @note Because GLFW is a C-style API, we can't pass a poiner to a class method, so we have to do it this way! - /// @param window [in] The glfw window. - /// @param key [in] The key which was pressed or released. - /// @param scancode [in] The system-specific scancode of the key. - /// @param action [in] The key action: GLFW_PRESS, GLFW_RELEASE or GLFW_REPEAT. - /// @param mods [in] Bit field describing which modifier keys were held down. - /// @TODO Avoid static methods! Poll the events manually in the render loop! - static void keyboard_input_callback_reloader(GLFWwindow* window, int key, int scancode, int action, int mods) +namespace inexor +{ + namespace vulkan_renderer { - auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); - app->keyboard_input_callback(window, key, scancode, action, mods); - } - - VkResult InexorApplication::load_TOML_configuration_file(const std::string& TOML_file_name) - { - spdlog::debug("Loading TOML configuration file: '{}'.", TOML_file_name); - std::ifstream toml_file; + /// @brief Static callback for window resize events. + /// @note Because GLFW is a C-style API, we can't pass a poiner to a class method, so we have to do it this way! + /// @param window The GLFW window. + /// @param height The width of the window. + /// @param height The height of the window. + /// @TODO Avoid static methods! Poll the events manually in the render loop! + static void frame_buffer_resize_callback(GLFWwindow* window, int width, int height) + { + spdlog::debug("Frame buffer resize callback called. window width: {}, height: {}", width, height); + + // This is actually the way it is handled by the official Vulkan samples. + auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); + app->frame_buffer_resized = true; + } - // Check if this file exists. - toml_file.open(TOML_file_name.c_str(), std::ios::in); - if(!toml_file.is_open()) + /// @brief Static callback for window resize events. + /// @note Because GLFW is a C-style API, we can't pass a poiner to a class method, so we have to do it this way! + /// @param window [in] The glfw window. + /// @param key [in] The key which was pressed or released. + /// @param scancode [in] The system-specific scancode of the key. + /// @param action [in] The key action: GLFW_PRESS, GLFW_RELEASE or GLFW_REPEAT. + /// @param mods [in] Bit field describing which modifier keys were held down. + /// @TODO Avoid static methods! Poll the events manually in the render loop! + static void keyboard_input_callback_reloader(GLFWwindow* window, int key, int scancode, int action, int mods) { - spdlog::error("Could not open configuration file: '{}'!", TOML_file_name); - return VK_ERROR_INITIALIZATION_FAILED; + auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); + app->keyboard_input_callback(window, key, scancode, action, mods); } - toml_file.close(); - // Load the TOML file using toml11. - auto renderer_configuration = toml::parse(TOML_file_name); + VkResult InexorApplication::load_TOML_configuration_file(const std::string& TOML_file_name) + { + spdlog::debug("Loading TOML configuration file: '{}'.", TOML_file_name); - // Search for the title of the configuration file and print it to debug output. - auto configuration_title = toml::find(renderer_configuration, "title"); - spdlog::debug("Title: '{}'", configuration_title); + std::ifstream toml_file; - window_width = toml::find(renderer_configuration, "application", "window", "width"); - window_height = toml::find(renderer_configuration, "application", "window", "width"); - window_title = toml::find(renderer_configuration, "application", "window", "name"); + // Check if this file exists. + toml_file.open(TOML_file_name.c_str(), std::ios::in); - spdlog::debug("Window: '{}', {} x {}", window_title, window_width, window_height); + if(!toml_file.is_open()) + { + spdlog::error("Could not open configuration file: '{}'!", TOML_file_name); + return VK_ERROR_INITIALIZATION_FAILED; + } - application_name = toml::find(renderer_configuration, "application", "name"); - engine_name = toml::find(renderer_configuration, "application", "engine", "name"); + toml_file.close(); - spdlog::debug("Application name: '{}'.", application_name); - - spdlog::debug("engine name: '{}'", engine_name); + // Load the TOML file using toml11. + auto renderer_configuration = toml::parse(TOML_file_name); - int application_version_major = toml::find(renderer_configuration, "application", "version", "major"); - int application_version_minor = toml::find(renderer_configuration, "application", "version", "minor"); - int application_version_patch = toml::find(renderer_configuration, "application", "version", "patch"); + // Search for the title of the configuration file and print it to debug output. + auto configuration_title = toml::find(renderer_configuration, "title"); + spdlog::debug("Title: '{}'", configuration_title); - spdlog::debug("Application version {}.{}.{}", application_version_major, application_version_minor, application_version_patch); + window_width = toml::find(renderer_configuration, "application", "window", "width"); + window_height = toml::find(renderer_configuration, "application", "window", "width"); + window_title = toml::find(renderer_configuration, "application", "window", "name"); - // Generate an uint32_t value from the major, minor and patch version info. - application_version = VK_MAKE_VERSION(application_version_major, application_version_minor, application_version_patch); - - int engine_version_major = toml::find(renderer_configuration, "application", "engine", "version", "major"); - int engine_version_minor = toml::find(renderer_configuration, "application", "engine", "version", "minor"); - int engine_version_patch = toml::find(renderer_configuration, "application", "engine", "version", "patch"); + spdlog::debug("Window: '{}', {} x {}", window_title, window_width, window_height); - spdlog::debug("Engine version {}.{}.{}", engine_version_major, engine_version_minor, engine_version_patch); + application_name = toml::find(renderer_configuration, "application", "name"); + engine_name = toml::find(renderer_configuration, "application", "engine", "name"); - // Generate an uint32_t value from the major, minor and patch version info. - engine_version = VK_MAKE_VERSION(engine_version_major, engine_version_minor, engine_version_patch); + spdlog::debug("Application name: '{}'.", application_name); - texture_files = toml::find>(renderer_configuration, "textures", "files"); - - spdlog::debug("Textures:"); + spdlog::debug("engine name: '{}'", engine_name); - for(const auto& texture_file : texture_files) - { - spdlog::debug("{}", texture_file); - } + int application_version_major = toml::find(renderer_configuration, "application", "version", "major"); + int application_version_minor = toml::find(renderer_configuration, "application", "version", "minor"); + int application_version_patch = toml::find(renderer_configuration, "application", "version", "patch"); - gltf_model_files = toml::find>(renderer_configuration, "glTFmodels", "files"); - - spdlog::debug("glTF 2.0 models:"); + spdlog::debug("Application version {}.{}.{}", application_version_major, application_version_minor, application_version_patch); - for(const auto& gltf_model_file : gltf_model_files) - { - spdlog::debug("{}", gltf_model_file); - } + // Generate an uint32_t value from the major, minor and patch version info. + application_version = VK_MAKE_VERSION(application_version_major, application_version_minor, application_version_patch); - vertex_shader_files = toml::find>(renderer_configuration, "shaders", "vertex", "files"); - - spdlog::debug("Vertex shaders:"); + int engine_version_major = toml::find(renderer_configuration, "application", "engine", "version", "major"); + int engine_version_minor = toml::find(renderer_configuration, "application", "engine", "version", "minor"); + int engine_version_patch = toml::find(renderer_configuration, "application", "engine", "version", "patch"); - for(const auto& vertex_shader_file : vertex_shader_files) - { - spdlog::debug("{}", vertex_shader_file); - } - - fragment_shader_files = toml::find>(renderer_configuration, "shaders", "fragment", "files"); - - spdlog::debug("Fragment shaders:"); + spdlog::debug("Engine version {}.{}.{}", engine_version_major, engine_version_minor, engine_version_patch); - for(const auto& fragment_shader_file : fragment_shader_files) - { - spdlog::debug("{}", fragment_shader_file); - } + // Generate an uint32_t value from the major, minor and patch version info. + engine_version = VK_MAKE_VERSION(engine_version_major, engine_version_minor, engine_version_patch); - // TODO: Load more info from TOML file. + texture_files = toml::find>(renderer_configuration, "textures", "files"); - return VK_SUCCESS; - } + spdlog::debug("Textures:"); + for(const auto& texture_file : texture_files) + { + spdlog::debug("{}", texture_file); + } - VkResult InexorApplication::load_textures() - { - assert(device); - assert(selected_graphics_card); - assert(debug_marker_manager); - assert(vma_allocator); - - VkResult result = texture_manager->initialise(device, selected_graphics_card, debug_marker_manager, vma_allocator, gpu_queue_manager->get_graphics_family_index().value(), gpu_queue_manager->get_graphics_queue()); - vulkan_error_check(result); - - // TODO: Refactor! use key from TOML file as name! - std::size_t texture_number = 1; - - for(const auto& texture_file : texture_files) - { - std::string texture_name = "example_texture_"+ std::to_string(texture_number); - texture_number++; + gltf_model_files = toml::find>(renderer_configuration, "glTFmodels", "files"); - std::shared_ptr new_texture; + spdlog::debug("glTF 2.0 models:"); - // TODO: Find duplicate loads! - // TOOD: Specify assets folder! - texture_manager->create_texture_from_file(texture_name, texture_file, new_texture); - vulkan_error_check(result); + for(const auto& gltf_model_file : gltf_model_files) + { + spdlog::debug("{}", gltf_model_file); + } - textures.push_back(new_texture); - } - - return VK_SUCCESS; - } + vertex_shader_files = toml::find>(renderer_configuration, "shaders", "vertex", "files"); + spdlog::debug("Vertex shaders:"); - VkResult InexorApplication::load_shaders() - { - assert(device); + for(const auto& vertex_shader_file : vertex_shader_files) + { + spdlog::debug("{}", vertex_shader_file); + } - spdlog::debug("Loading vertex shaders."); + fragment_shader_files = toml::find>(renderer_configuration, "shaders", "fragment", "files"); - if(0 == vertex_shader_files.size()) - { - spdlog::error("No vertex shaders to load!"); + spdlog::debug("Fragment shaders:"); + + for(const auto& fragment_shader_file : fragment_shader_files) + { + spdlog::debug("{}", fragment_shader_file); + } + + // TODO: Load more info from TOML file. + + return VK_SUCCESS; } - // Loop through the list of vertex shaders and initialise all of them. - for(const auto& vertex_shader : vertex_shader_files) + + VkResult InexorApplication::load_textures() { - spdlog::debug("Loading vertex shader file {}.", vertex_shader); - - VkResult result = shader_manager->create_shader_from_file(VK_SHADER_STAGE_VERTEX_BIT, vertex_shader, vertex_shader, "main"); - if(VK_SUCCESS != result) + assert(device); + assert(selected_graphics_card); + assert(debug_marker_manager); + assert(vma_allocator); + + VkResult result = texture_manager->initialise(device, selected_graphics_card, debug_marker_manager, vma_allocator, gpu_queue_manager->get_graphics_family_index().value(), gpu_queue_manager->get_graphics_queue()); + vulkan_error_check(result); + + // TODO: Refactor! use key from TOML file as name! + std::size_t texture_number = 1; + + for(const auto& texture_file : texture_files) { + std::string texture_name = "example_texture_" + std::to_string(texture_number); + texture_number++; + + std::shared_ptr new_texture; + + // TODO: Find duplicate loads! + // TOOD: Specify assets folder! + texture_manager->create_texture_from_file(texture_name, texture_file, new_texture); vulkan_error_check(result); - std::string error_message = "Error: Could not initialise vertex shader " + vertex_shader; - display_error_message(error_message); - exit(-1); + + textures.push_back(new_texture); } + + return VK_SUCCESS; } - spdlog::debug("Loading fragment shaders."); - if(0 == fragment_shader_files.size()) + VkResult InexorApplication::load_shaders() { - spdlog::error("No fragment shaders to load!"); + assert(device); + + spdlog::debug("Loading vertex shaders."); + + if(0 == vertex_shader_files.size()) + { + spdlog::error("No vertex shaders to load!"); + } + + // Loop through the list of vertex shaders and initialise all of them. + for(const auto& vertex_shader : vertex_shader_files) + { + spdlog::debug("Loading vertex shader file {}.", vertex_shader); + + VkResult result = shader_manager->create_shader_from_file(VK_SHADER_STAGE_VERTEX_BIT, vertex_shader, vertex_shader, "main"); + if(VK_SUCCESS != result) + { + vulkan_error_check(result); + std::string error_message = "Error: Could not initialise vertex shader " + vertex_shader; + display_error_message(error_message); + exit(-1); + } + } + + spdlog::debug("Loading fragment shaders."); + + if(0 == fragment_shader_files.size()) + { + spdlog::error("No fragment shaders to load!"); + } + + // Loop through the list of fragment shaders and initialise all of them. + for(const auto& fragment_shader : fragment_shader_files) + { + spdlog::debug("Loading fragment shader file {}.", fragment_shader); + + VkResult result = shader_manager->create_shader_from_file(VK_SHADER_STAGE_FRAGMENT_BIT, fragment_shader, fragment_shader, "main"); + if(VK_SUCCESS != result) + { + vulkan_error_check(result); + std::string error_message = "Error: Could not initialise fragment shader " + fragment_shader; + display_error_message(error_message); + exit(-1); + } + } + + spdlog::debug("Loading shaders finished."); + + return VK_SUCCESS; } - // Loop through the list of fragment shaders and initialise all of them. - for(const auto& fragment_shader : fragment_shader_files) + + /// TODO: Refactor rendering method! + /// TODO: Finish present call using transfer queue. + VkResult InexorApplication::render_frame() { - spdlog::debug("Loading fragment shader file {}.", fragment_shader); - - VkResult result = shader_manager->create_shader_from_file(VK_SHADER_STAGE_FRAGMENT_BIT, fragment_shader, fragment_shader, "main"); - if(VK_SUCCESS != result) + assert(device); + assert(gpu_queue_manager->get_graphics_queue()); + assert(gpu_queue_manager->get_present_queue()); + + vkWaitForFences(device, 1, &(*in_flight_fences[current_frame]), VK_TRUE, UINT64_MAX); + + uint32_t image_index = 0; + VkResult result = vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, *image_available_semaphores[current_frame], VK_NULL_HANDLE, &image_index); + + if(VK_NULL_HANDLE != images_in_flight[image_index]) { - vulkan_error_check(result); - std::string error_message = "Error: Could not initialise fragment shader " + fragment_shader; + vkWaitForFences(device, 1, &*images_in_flight[image_index], VK_TRUE, UINT64_MAX); + } + + // Update the data which changes every frame! + update_uniform_buffers(current_frame); + + // Mark the image as now being in use by this frame. + images_in_flight[image_index] = in_flight_fences[current_frame]; + + // Is it time to regenerate the swapchain because window has been resized or minimized? + if(VK_ERROR_OUT_OF_DATE_KHR == result) + { + // VK_ERROR_OUT_OF_DATE_KHR: The swap chain has become incompatible with the surface + // and can no longer be used for rendering. Usually happens after a window resize. + return recreate_swapchain(); + } + + // Did something else fail? + // VK_SUBOPTIMAL_KHR: The swap chain can still be used to successfully present + // to the surface, but the surface properties are no longer matched exactly. + if(VK_SUCCESS != result && VK_SUBOPTIMAL_KHR != result) + { + std::string error_message = "Error: Failed to acquire swapchain image!"; display_error_message(error_message); exit(-1); } - } - spdlog::debug("Loading shaders finished."); + const VkPipelineStageFlags wait_stage_mask[] = + { + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT + }; + + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.pNext = nullptr; + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitDstStageMask = wait_stage_mask; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &command_buffers[image_index]; + submit_info.signalSemaphoreCount = 1; + submit_info.pWaitSemaphores = &*image_available_semaphores[current_frame]; + submit_info.pSignalSemaphores = &*rendering_finished_semaphores[current_frame]; + + vkResetFences(device, 1, &*in_flight_fences[current_frame]); + + result = vkQueueSubmit(gpu_queue_manager->get_graphics_queue(), 1, &submit_info, *in_flight_fences[current_frame]); + if(VK_SUCCESS != result) return result; + + present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present_info.pNext = nullptr; + present_info.waitSemaphoreCount = 1; + present_info.pWaitSemaphores = &*rendering_finished_semaphores[current_frame]; + present_info.swapchainCount = 1; + present_info.pSwapchains = &swapchain; + present_info.pImageIndices = &image_index; + present_info.pResults = nullptr; + + result = vkQueuePresentKHR(gpu_queue_manager->get_present_queue(), &present_info); + + // Some notes on frame_buffer_resized: + // It is important to do this after vkQueuePresentKHR to ensure that the semaphores are + // in a consistent state, otherwise a signalled semaphore may never be properly waited upon. + if(VK_ERROR_OUT_OF_DATE_KHR == result || VK_SUBOPTIMAL_KHR == result || frame_buffer_resized) + { + frame_buffer_resized = false; + recreate_swapchain(); + } - return VK_SUCCESS; - } + current_frame = (current_frame + 1) % INEXOR_MAX_FRAMES_IN_FLIGHT; + auto fps_value = fps_counter.update(); - /// TODO: Refactor rendering method! - /// TODO: Finish present call using transfer queue. - VkResult InexorApplication::render_frame() - { - assert(device); - assert(gpu_queue_manager->get_graphics_queue()); - assert(gpu_queue_manager->get_present_queue()); + if(fps_value.has_value()) + { + // Update fps by changing window name. + std::string window_title = "Inexor Vulkan API renderer demo - " + std::to_string(fps_value.value()) + " FPS"; + glfwSetWindowTitle(window, window_title.c_str()); + } - vkWaitForFences(device, 1, &(*in_flight_fences[current_frame]), VK_TRUE, UINT64_MAX); - uint32_t image_index = 0; - VkResult result = vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, *image_available_semaphores[current_frame], VK_NULL_HANDLE, &image_index); - - if(VK_NULL_HANDLE != images_in_flight[image_index]) - { - vkWaitForFences(device, 1, &*images_in_flight[image_index], VK_TRUE, UINT64_MAX); + return VK_SUCCESS; } - - // Update the data which changes every frame! - update_uniform_buffers(current_frame); - // Mark the image as now being in use by this frame. - images_in_flight[image_index] = in_flight_fences[current_frame]; - // Is it time to regenerate the swapchain because window has been resized or minimized? - if(VK_ERROR_OUT_OF_DATE_KHR == result) + VkResult InexorApplication::load_models() { - // VK_ERROR_OUT_OF_DATE_KHR: The swap chain has become incompatible with the surface - // and can no longer be used for rendering. Usually happens after a window resize. - return recreate_swapchain(); - } + assert(debug_marker_manager); - // Did something else fail? - // VK_SUBOPTIMAL_KHR: The swap chain can still be used to successfully present - // to the surface, but the surface properties are no longer matched exactly. - if(VK_SUCCESS != result && VK_SUBOPTIMAL_KHR != result) - { - std::string error_message = "Error: Failed to acquire swapchain image!"; - display_error_message(error_message); - exit(-1); - } + spdlog::debug("Loading models."); - const VkPipelineStageFlags wait_stage_mask[] = - { - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT - }; - - submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit_info.pNext = nullptr; - submit_info.waitSemaphoreCount = 1; - submit_info.pWaitDstStageMask = wait_stage_mask; - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &command_buffers[image_index]; - submit_info.signalSemaphoreCount = 1; - submit_info.pWaitSemaphores = &*image_available_semaphores[current_frame]; - submit_info.pSignalSemaphores = &*rendering_finished_semaphores[current_frame]; - - vkResetFences(device, 1, &*in_flight_fences[current_frame]); - - result = vkQueueSubmit(gpu_queue_manager->get_graphics_queue(), 1, &submit_info, *in_flight_fences[current_frame]); - if(VK_SUCCESS != result) return result; - - present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - present_info.pNext = nullptr; - present_info.waitSemaphoreCount = 1; - present_info.pWaitSemaphores = &*rendering_finished_semaphores[current_frame]; - present_info.swapchainCount = 1; - present_info.pSwapchains = &swapchain; - present_info.pImageIndices = &image_index; - present_info.pResults = nullptr; - - result = vkQueuePresentKHR(gpu_queue_manager->get_present_queue(), &present_info); - - // Some notes on frame_buffer_resized: - // It is important to do this after vkQueuePresentKHR to ensure that the semaphores are - // in a consistent state, otherwise a signalled semaphore may never be properly waited upon. - if(VK_ERROR_OUT_OF_DATE_KHR == result || VK_SUBOPTIMAL_KHR == result || frame_buffer_resized) - { - frame_buffer_resized = false; - recreate_swapchain(); - } - - current_frame = (current_frame + 1) % INEXOR_MAX_FRAMES_IN_FLIGHT; + // TODO: Load models from TOML list. + gltf_model_manager->load_model_from_glTF2_file("inexor_logo", "assets/models/inexor/inexor_2.gltf"); - auto fps_value = fps_counter.update(); + spdlog::debug("Loading models finished."); - if(fps_value.has_value()) - { - // Update fps by changing window name. - std::string window_title = "Inexor Vulkan API renderer demo - "+ std::to_string(fps_value.value()) +" FPS"; - glfwSetWindowTitle(window, window_title.c_str()); + return VK_SUCCESS; } - return VK_SUCCESS; - } - - - VkResult InexorApplication::load_models() - { - assert(debug_marker_manager); - - spdlog::debug("Loading models."); - - // TODO: Load models from TOML list. - gltf_model_manager->load_model_from_glTF2_file("inexor_logo", "assets/models/inexor/inexor.gltf"); + VkResult InexorApplication::check_application_specific_features() + { + assert(selected_graphics_card); - spdlog::debug("Loading models finished."); + VkPhysicalDeviceFeatures graphics_card_features; - return VK_SUCCESS; - } + vkGetPhysicalDeviceFeatures(selected_graphics_card, &graphics_card_features); + // Check if anisotropic filtering is available! + if(!graphics_card_features.samplerAnisotropy) + { + spdlog::warn("The selected graphics card does not support anisotropic filtering!"); + } + else + { + spdlog::debug("The selected graphics card does support anisotropic filtering."); + } - VkResult InexorApplication::check_application_specific_features() - { - assert(selected_graphics_card); + // TODO: Add more checks if necessary. - VkPhysicalDeviceFeatures graphics_card_features; + return VK_SUCCESS; + } - vkGetPhysicalDeviceFeatures(selected_graphics_card, &graphics_card_features); - // Check if anisotropic filtering is available! - if(!graphics_card_features.samplerAnisotropy) - { - spdlog::warn("The selected graphics card does not support anisotropic filtering!"); - } - else + VkResult InexorApplication::initialise() { - spdlog::debug("The selected graphics card does support anisotropic filtering."); - } + spdlog::debug("Initialising vulkan-renderer."); - // TODO: Add more checks if necessary. + spdlog::debug("Initialising thread-pool with {} threads.", std::thread::hardware_concurrency()); - return VK_SUCCESS; - } + // TOOD: Implement -threads command line argument. + // Initialise Inexor thread-pool. + thread_pool = std::make_shared(); - VkResult InexorApplication::initialise() - { - spdlog::debug("Initialising vulkan-renderer."); - - spdlog::debug("Initialising thread-pool with {} threads.", std::thread::hardware_concurrency()); + // Load the configuration from the TOML file. + VkResult result = load_TOML_configuration_file("configuration/renderer.toml"); + vulkan_error_check(result); - // TOOD: Implement -threads command line argument. - - // Initialise Inexor thread-pool. - thread_pool = std::make_shared(); + spdlog::debug("Creating window."); - // Load the configuration from the TOML file. - VkResult result = load_TOML_configuration_file("configuration/renderer.toml"); - vulkan_error_check(result); + // Initialise GLFW library. + glfwInit(); - spdlog::debug("Creating window."); + // We do not want to use the OpenGL API. + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - // Initialise GLFW library. - glfwInit(); + glfwWindowHint(GLFW_VISIBLE, true); - // We do not want to use the OpenGL API. - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); - glfwWindowHint(GLFW_VISIBLE, true); + // Create the window using GLFW library. + window = glfwCreateWindow(window_width, window_height, window_title.c_str(), nullptr, nullptr); - glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + spdlog::debug("Storing GLFW window user pointer."); - // Create the window using GLFW library. - window = glfwCreateWindow(window_width, window_height, window_title.c_str(), nullptr, nullptr); + // Store the current InexorApplication instance in the GLFW window user pointer. + // Since GLFW is a C-style API, we can't use a class method as callback for window resize! + // TODO: Refactor! Don't use callback functions! use manual polling in the render loop instead. + glfwSetWindowUserPointer(window, this); - spdlog::debug("Storing GLFW window user pointer."); + spdlog::debug("Setting up framebuffer resize callback."); - // Store the current InexorApplication instance in the GLFW window user pointer. - // Since GLFW is a C-style API, we can't use a class method as callback for window resize! - // TODO: Refactor! Don't use callback functions! use manual polling in the render loop instead. - glfwSetWindowUserPointer(window, this); + // Setup callback for window resize. + // Since GLFW is a C-style API, we can't use a class method as callback for window resize! + glfwSetFramebufferSizeCallback(window, frame_buffer_resize_callback); - spdlog::debug("Setting up framebuffer resize callback."); + spdlog::debug("Checking for '-renderdoc' command line argument."); - // Setup callback for window resize. - // Since GLFW is a C-style API, we can't use a class method as callback for window resize! - glfwSetFramebufferSizeCallback(window, frame_buffer_resize_callback); + bool enable_renderdoc_instance_layer = false; - spdlog::debug("Checking for '-renderdoc' command line argument."); + // If the user specified command line argument "-renderdoc", the RenderDoc instance layer will be enabled. + std::optional enable_renderdoc = is_command_line_argument_specified("-renderdoc"); - bool enable_renderdoc_instance_layer = false; - - // If the user specified command line argument "-renderdoc", the RenderDoc instance layer will be enabled. - std::optional enable_renderdoc = is_command_line_argument_specified("-renderdoc"); - - if(enable_renderdoc.has_value()) - { - if(enable_renderdoc.value()) + if(enable_renderdoc.has_value()) { - spdlog::debug("RenderDoc command line argument specified."); - enable_renderdoc_instance_layer = true; + if(enable_renderdoc.value()) + { + spdlog::debug("RenderDoc command line argument specified."); + enable_renderdoc_instance_layer = true; + } } - } - - spdlog::debug("Checking for '-novalidation' command line argument."); - - bool enable_khronos_validation_instance_layer = true; - // If the user specified command line argument "-novalidation", the Khronos validation instance layer will be disabled. - // For debug builds, this is not advisable! Always use validation layers during development! - std::optional disable_validation = is_command_line_argument_specified("-novalidation"); + spdlog::debug("Checking for '-novalidation' command line argument."); - if(disable_validation.has_value()) - { - if(disable_validation.value()) + bool enable_khronos_validation_instance_layer = true; + + // If the user specified command line argument "-novalidation", the Khronos validation instance layer will be disabled. + // For debug builds, this is not advisable! Always use validation layers during development! + std::optional disable_validation = is_command_line_argument_specified("-novalidation"); + + if(disable_validation.has_value()) { - spdlog::warn("Vulkan validation layers DISABLED by command line argument -novalidation!."); - enable_khronos_validation_instance_layer = false; + if(disable_validation.value()) + { + spdlog::warn("Vulkan validation layers DISABLED by command line argument -novalidation!."); + enable_khronos_validation_instance_layer = false; + } } - } - spdlog::debug("Creating Vulkan instance."); + spdlog::debug("Creating Vulkan instance."); - result = create_vulkan_instance(application_name, engine_name, application_version, engine_version, enable_khronos_validation_instance_layer, enable_renderdoc_instance_layer); - vulkan_error_check(result); - - // Check if validation is enabled check for availabiliy of VK_EXT_debug_utils. - if(enable_khronos_validation_instance_layer) - { - spdlog::debug("Khronos validation layer is enabled."); + result = create_vulkan_instance(application_name, engine_name, application_version, engine_version, enable_khronos_validation_instance_layer, enable_renderdoc_instance_layer); + vulkan_error_check(result); - if(availability_checks_manager->is_instance_extension_available(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) + // Check if validation is enabled check for availabiliy of VK_EXT_debug_utils. + if(enable_khronos_validation_instance_layer) { - VkDebugReportCallbackCreateInfoEXT debug_report_create_info = {}; - - debug_report_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; - debug_report_create_info.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT; - debug_report_create_info.pfnCallback = (PFN_vkDebugReportCallbackEXT)&VulkanDebugMessageCallback; - - // We have to explicitly load this function. - PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT")); + spdlog::debug("Khronos validation layer is enabled."); - if(nullptr != vkCreateDebugReportCallbackEXT) + if(availability_checks_manager->is_instance_extension_available(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) { - // Create the debug report callback. - VkResult result = vkCreateDebugReportCallbackEXT(instance, &debug_report_create_info, nullptr, &debug_report_callback); - if(VK_SUCCESS == result) + VkDebugReportCallbackCreateInfoEXT debug_report_create_info = {}; + + debug_report_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; + debug_report_create_info.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT; + debug_report_create_info.pfnCallback = (PFN_vkDebugReportCallbackEXT)&VulkanDebugMessageCallback; + + // We have to explicitly load this function. + PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT")); + + if(nullptr != vkCreateDebugReportCallbackEXT) { - spdlog::debug("Creating Vulkan debug callback."); - debug_report_callback_initialised = true; + // Create the debug report callback. + VkResult result = vkCreateDebugReportCallbackEXT(instance, &debug_report_create_info, nullptr, &debug_report_callback); + if(VK_SUCCESS == result) + { + spdlog::debug("Creating Vulkan debug callback."); + debug_report_callback_initialised = true; + } + else + { + vulkan_error_check(result); + } } else { - vulkan_error_check(result); + spdlog::error("vkCreateDebugReportCallbackEXT is a null-pointer! Function not available."); } } else { - spdlog::error("vkCreateDebugReportCallbackEXT is a null-pointer! Function not available."); + spdlog::warn("Khronos validation layer is not available!"); } } else { - spdlog::warn("Khronos validation layer is not available!"); + spdlog::warn("Khronos validation layer is DISABLED."); } - } - else - { - spdlog::warn("Khronos validation layer is DISABLED."); - } - - spdlog::debug("Creating window surface."); - - // Create a window surface using GLFW library. - // The window surface needs to be created right after the instance creation, - // because it can actually influence the physical device selection. - result = create_window_surface(instance, window, surface); - if(VK_SUCCESS != result) - { - vulkan_error_check(result); - return result; - } - - spdlog::debug("Checking for -gpu command line argument."); - // The user can specify with "-gpu " which graphics card to prefer. - std::optional prefered_graphics_card = get_command_line_argument_uint32_t("-gpu"); + spdlog::debug("Creating window surface."); - if(prefered_graphics_card.has_value()) - { - spdlog::debug("Preferential graphics card index {} specified.", prefered_graphics_card.value()); - } + // Create a window surface using GLFW library. + // The window surface needs to be created right after the instance creation, + // because it can actually influence the physical device selection. + result = create_window_surface(instance, window, surface); + if(VK_SUCCESS != result) + { + vulkan_error_check(result); + return result; + } - // Let's see if there is a graphics card that is suitable for us. - std::optional graphics_card_candidate = settings_decision_maker->decide_which_graphics_card_to_use(instance, surface, prefered_graphics_card); + spdlog::debug("Checking for -gpu command line argument."); - // Check if we found a graphics card candidate. - if(graphics_card_candidate.has_value()) - { - selected_graphics_card = graphics_card_candidate.value(); - } - else - { - // No graphics card suitable! - std::string error_message = "Error: Could not find any suitable GPU!"; - display_fatal_error_message(error_message); - return VK_ERROR_INITIALIZATION_FAILED; - } + // The user can specify with "-gpu " which graphics card to prefer. + std::optional prefered_graphics_card = get_command_line_argument_uint32_t("-gpu"); - bool display_graphics_card_info = true; - - spdlog::debug("Checking for -nostats command line argument."); + if(prefered_graphics_card.has_value()) + { + spdlog::debug("Preferential graphics card index {} specified.", prefered_graphics_card.value()); + } - // If the user specified command line argument "-nostats", no information will be - // displayed about all the graphics cards which are available on the system. - std::optional hide_gpu_stats = is_command_line_argument_specified("-nostats"); - - if(hide_gpu_stats.has_value()) - { - if(hide_gpu_stats.value()) + // Let's see if there is a graphics card that is suitable for us. + std::optional graphics_card_candidate = settings_decision_maker->decide_which_graphics_card_to_use(instance, surface, prefered_graphics_card); + + // Check if we found a graphics card candidate. + if(graphics_card_candidate.has_value()) { - spdlog::debug("No extended information about graphics cards will be shown."); - display_graphics_card_info = false; + selected_graphics_card = graphics_card_candidate.value(); + } + else + { + // No graphics card suitable! + std::string error_message = "Error: Could not find any suitable GPU!"; + display_fatal_error_message(error_message); + return VK_ERROR_INITIALIZATION_FAILED; } - } - if(display_graphics_card_info) - { - spdlog::debug("Displaying extended information about graphics cards."); - - // Print general information about Vulkan. - gpu_info_manager->print_driver_vulkan_version(); - gpu_info_manager->print_instance_layers(); - gpu_info_manager->print_instance_extensions(); - - // Print all information that we can find about all graphics card available. - gpu_info_manager->print_all_physical_devices(instance, surface); - } - - spdlog::debug("Checking for -no_separate_data_queue command line argument."); + bool display_graphics_card_info = true; - // Ignore distinct data transfer queue. - std::optional forbid_distinct_data_transfer_queue = is_command_line_argument_specified("-no_separate_data_queue"); + spdlog::debug("Checking for -nostats command line argument."); - bool use_distinct_data_transfer_queue = true; + // If the user specified command line argument "-nostats", no information will be + // displayed about all the graphics cards which are available on the system. + std::optional hide_gpu_stats = is_command_line_argument_specified("-nostats"); - if(forbid_distinct_data_transfer_queue.has_value()) - { - if(forbid_distinct_data_transfer_queue.value()) + if(hide_gpu_stats.has_value()) { - spdlog::warn("Command line argument -no_separate_data_queue specified."); - spdlog::warn("This will force the application to avoid using a distinct queue for data transfer to GPU."); - spdlog::warn("Performance loss might be a result of this!"); - use_distinct_data_transfer_queue = false; + if(hide_gpu_stats.value()) + { + spdlog::debug("No extended information about graphics cards will be shown."); + display_graphics_card_info = false; + } } - } - result = gpu_queue_manager->initialise(settings_decision_maker); - vulkan_error_check(result); + if(display_graphics_card_info) + { + spdlog::debug("Displaying extended information about graphics cards."); - result = gpu_queue_manager->prepare_queues(selected_graphics_card, surface, use_distinct_data_transfer_queue); - vulkan_error_check(result); + // Print general information about Vulkan. + gpu_info_manager->print_driver_vulkan_version(); + gpu_info_manager->print_instance_layers(); + gpu_info_manager->print_instance_extensions(); - spdlog::debug("Checking for -no_vk_debug_markers command line argument."); + // Print all information that we can find about all graphics card available. + gpu_info_manager->print_all_physical_devices(instance, surface); + } - bool enable_debug_marker_device_extension = true; + spdlog::debug("Checking for -no_separate_data_queue command line argument."); - if(!enable_renderdoc_instance_layer) - { - // Debug markers are only available if RenderDoc is enabled. - enable_debug_marker_device_extension = false; - } + // Ignore distinct data transfer queue. + std::optional forbid_distinct_data_transfer_queue = is_command_line_argument_specified("-no_separate_data_queue"); - // Check if Vulkan debug markers should be disabled. - // Those are only available if RenderDoc instance layer is enabled! - std::optional no_vulkan_debug_markers = is_command_line_argument_specified("-no_vk_debug_markers"); - - if(no_vulkan_debug_markers.has_value()) - { - if(no_vulkan_debug_markers.value()) + bool use_distinct_data_transfer_queue = true; + + if(forbid_distinct_data_transfer_queue.has_value()) { - spdlog::warn("Vulkan debug markers are disabled because -no_vk_debug_markers was specified."); + if(forbid_distinct_data_transfer_queue.value()) + { + spdlog::warn("Command line argument -no_separate_data_queue specified."); + spdlog::warn("This will force the application to avoid using a distinct queue for data transfer to GPU."); + spdlog::warn("Performance loss might be a result of this!"); + use_distinct_data_transfer_queue = false; + } + } + + result = gpu_queue_manager->initialise(settings_decision_maker); + vulkan_error_check(result); + + result = gpu_queue_manager->prepare_queues(selected_graphics_card, surface, use_distinct_data_transfer_queue); + vulkan_error_check(result); + + spdlog::debug("Checking for -no_vk_debug_markers command line argument."); + + bool enable_debug_marker_device_extension = true; + + if(!enable_renderdoc_instance_layer) + { + // Debug markers are only available if RenderDoc is enabled. enable_debug_marker_device_extension = false; } - } - result = create_physical_device(selected_graphics_card, enable_debug_marker_device_extension); - vulkan_error_check(result); + // Check if Vulkan debug markers should be disabled. + // Those are only available if RenderDoc instance layer is enabled! + std::optional no_vulkan_debug_markers = is_command_line_argument_specified("-no_vk_debug_markers"); - // Assign an appropriate name to the central Vulkan device. - // Debug markers are very useful when debugging vulkan-renderer with RenderDoc! - //debug_marker_manager->set_object_name(device, (uint64_t)(device), VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT, "Inexor Vulkan device."); + if(no_vulkan_debug_markers.has_value()) + { + if(no_vulkan_debug_markers.value()) + { + spdlog::warn("Vulkan debug markers are disabled because -no_vk_debug_markers was specified."); + enable_debug_marker_device_extension = false; + } + } - result = gltf_model_manager->initialise(device, texture_manager, uniform_buffer_manager, mesh_buffer_manager, descriptor_manager); - vulkan_error_check(result); + result = create_physical_device(selected_graphics_card, enable_debug_marker_device_extension); + vulkan_error_check(result); - result = check_application_specific_features(); - vulkan_error_check(result); + // Assign an appropriate name to the central Vulkan device. + // Debug markers are very useful when debugging vulkan-renderer with RenderDoc! + //debug_marker_manager->set_object_name(device, (uint64_t)(device), VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT, "Inexor Vulkan device."); - // Vulkan debug markes will be very useful when debugging with RenderDoc! - result = initialise_debug_marker_manager(enable_debug_marker_device_extension); - vulkan_error_check(result); - - result = shader_manager->initialise(device, debug_marker_manager); - vulkan_error_check(result); + result = gltf_model_manager->initialise(device, texture_manager, uniform_buffer_manager, mesh_buffer_manager, descriptor_manager); + vulkan_error_check(result); - result = create_vma_allocator(); - vulkan_error_check(result); + result = check_application_specific_features(); + vulkan_error_check(result); - result = gpu_queue_manager->setup_queues(device); - vulkan_error_check(result); + // Vulkan debug markes will be very useful when debugging with RenderDoc! + result = initialise_debug_marker_manager(enable_debug_marker_device_extension); + vulkan_error_check(result); - result = create_swapchain(); - vulkan_error_check(result); + result = shader_manager->initialise(device, debug_marker_manager); + vulkan_error_check(result); - result = create_depth_buffer(); - vulkan_error_check(result); + result = create_vma_allocator(); + vulkan_error_check(result); - spdlog::debug("Starting to load textures using threadpool."); - - result = load_textures(); - vulkan_error_check(result); - - result = load_shaders(); - vulkan_error_check(result); + result = gpu_queue_manager->setup_queues(device); + vulkan_error_check(result); - result = descriptor_manager->initialise(device, number_of_images_in_swapchain, debug_marker_manager); - vulkan_error_check(result); + result = create_swapchain(); + vulkan_error_check(result); - result = create_descriptor_pool(); - vulkan_error_check(result); + result = create_depth_buffer(); + vulkan_error_check(result); - result = descriptor_manager->create_descriptor_bundle("inexor_global_descriptor_bundle", global_descriptor_pool, descriptor_bundles.scene); - vulkan_error_check(result); + spdlog::debug("Starting to load textures using threadpool."); - result = create_descriptor_set_layouts(); - vulkan_error_check(result); + result = load_textures(); + vulkan_error_check(result); - result = create_pipeline(); - vulkan_error_check(result); - - result = create_frame_buffers(); - vulkan_error_check(result); + result = load_shaders(); + vulkan_error_check(result); - result = mesh_buffer_manager->initialise(device, debug_marker_manager, vma_allocator, gpu_queue_manager->get_data_transfer_queue_family_index().value(), gpu_queue_manager->get_data_transfer_queue()); - vulkan_error_check(result); + result = descriptor_manager->initialise(device, number_of_images_in_swapchain, debug_marker_manager); + vulkan_error_check(result); - result = create_command_pool(); - vulkan_error_check(result); + result = create_descriptor_pool(); + vulkan_error_check(result); - result = uniform_buffer_manager->initialise(device, vma_allocator, debug_marker_manager); - vulkan_error_check(result); + result = descriptor_manager->create_descriptor_bundle("inexor_global_descriptor_bundle", global_descriptor_pool, descriptor_bundles.scene); + vulkan_error_check(result); - result = create_uniform_buffers(); - vulkan_error_check(result); + result = create_descriptor_set_layouts(); + vulkan_error_check(result); - result = create_descriptor_writes(); - vulkan_error_check(result); + result = create_pipeline(); + vulkan_error_check(result); - result = create_descriptor_sets(); - vulkan_error_check(result); + result = create_frame_buffers(); + vulkan_error_check(result); - result = create_command_buffers(); - vulkan_error_check(result); + result = mesh_buffer_manager->initialise(device, debug_marker_manager, vma_allocator, gpu_queue_manager->get_data_transfer_queue_family_index().value(), gpu_queue_manager->get_data_transfer_queue()); + vulkan_error_check(result); - result = load_models(); - vulkan_error_check(result); + result = create_command_pool(); + vulkan_error_check(result); - result = gltf_model_manager->create_model_descriptors(number_of_images_in_swapchain); - vulkan_error_check(result); + result = uniform_buffer_manager->initialise(device, vma_allocator, debug_marker_manager); + vulkan_error_check(result); - result = record_command_buffers(); - vulkan_error_check(result); + result = create_uniform_buffers(); + vulkan_error_check(result); - result = fence_manager->initialise(device, debug_marker_manager); - vulkan_error_check(result); + result = create_descriptor_writes(); + vulkan_error_check(result); - result = semaphore_manager->initialise(device, debug_marker_manager); - vulkan_error_check(result); + result = create_descriptor_sets(); + vulkan_error_check(result); - result = create_synchronisation_objects(); - vulkan_error_check(result); + result = create_command_buffers(); + vulkan_error_check(result); - spdlog::debug("Vulkan initialisation finished."); + result = load_models(); + vulkan_error_check(result); - spdlog::debug("Showing window."); - - //glfwShowWindow(window); - - // We must store the window user pointer to be able to call the window resize callback. - // TODO: Use window queue instead? - glfwSetWindowUserPointer(window, this); + result = gltf_model_manager->create_model_descriptors(number_of_images_in_swapchain); + vulkan_error_check(result); - InexorKeyboardInputHandler::initialise(window, keyboard_input_callback_reloader); - - game_camera_1.set_position(glm::vec3(0.0f, 5.0f, 5.0f)); - game_camera_1.set_direction(glm::vec3(0.0f, 1.0f, 1.0f)); - game_camera_1.set_speed(0.5f); + result = record_command_buffers(); + vulkan_error_check(result); - game_camera_1.update(time_passed); + result = fence_manager->initialise(device, debug_marker_manager); + vulkan_error_check(result); - return VK_SUCCESS; - } + result = semaphore_manager->initialise(device, debug_marker_manager); + vulkan_error_check(result); + result = create_synchronisation_objects(); + vulkan_error_check(result); - VkResult InexorApplication::update_uniform_buffers(const std::size_t current_image) - { - float time = time_step.get_program_start_time_step(); + spdlog::debug("Vulkan initialisation finished."); - UniformBufferObject ubo = {}; - - // Rotate the model as a function of time. - ubo.model = glm::rotate(glm::mat4(1.0f), /*time */ glm::radians(0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + spdlog::debug("Showing window."); - ubo.view = game_camera_1.get_view_matrix(); - ubo.proj = game_camera_1.get_projection_matrix(); - - ubo.proj[1][1] *= -1; + //glfwShowWindow(window); - // Update the world matrices! - // TODO: Update by shared pointer value! - uniform_buffer_manager->update_uniform_buffer("matrices", &ubo, sizeof(ubo)); + // We must store the window user pointer to be able to call the window resize callback. + // TODO: Use window queue instead? + glfwSetWindowUserPointer(window, this); - return VK_SUCCESS; - } + InexorKeyboardInputHandler::initialise(window, keyboard_input_callback_reloader); + game_camera_1.set_position(glm::vec3(0.0f, 5.0f, -2.0f)); + game_camera_1.set_direction(glm::vec3(0.0f, 1.0f, 0.0f)); + game_camera_1.set_speed(1.0f); - void InexorApplication::keyboard_input_callback(GLFWwindow* window, int key, int scancode, int action, int mods) - { - // TODO: Abstract this! + game_camera_1.update(time_passed); - // Move camera forwards. - if(GLFW_KEY_W == key) + return VK_SUCCESS; + } + + + VkResult InexorApplication::update_uniform_buffers(const std::size_t current_image) { - if(GLFW_PRESS == action) - { - game_camera_1.start_camera_movement(); - } - if(GLFW_RELEASE == action) - { - game_camera_1.end_camera_movement(); - } + float time = time_step.get_program_start_time_step(); + + UniformBufferObject ubo = {}; + + // Rotate the model as a function of time. + ubo.model = glm::rotate(glm::mat4(1.0f), /*time */ glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f)); + + ubo.view = game_camera_1.get_view_matrix(); + ubo.proj = game_camera_1.get_projection_matrix(); + + ubo.proj[1][1] *= -1; + + // Update the world matrices! + // TODO: Update by shared pointer value! + uniform_buffer_manager->update_uniform_buffer("matrices", &ubo, sizeof(ubo)); + + return VK_SUCCESS; } - // Move camera backwards. - if(GLFW_KEY_S == key) + + void InexorApplication::keyboard_input_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { - if(GLFW_PRESS == action) + // TODO: Abstract this! + + // Move camera forwards. + if(GLFW_KEY_W == key) { - // true because we're moving backwards. - game_camera_1.start_camera_movement(true); + if(GLFW_PRESS == action) + { + game_camera_1.start_camera_movement(); + } + if(GLFW_RELEASE == action) + { + game_camera_1.end_camera_movement(); + } } - if(GLFW_RELEASE == action) + + // Move camera backwards. + if(GLFW_KEY_S == key) { - game_camera_1.end_camera_movement(); + if(GLFW_PRESS == action) + { + // true because we're moving backwards. + game_camera_1.start_camera_movement(true); + } + if(GLFW_RELEASE == action) + { + game_camera_1.end_camera_movement(); + } } } - } - - void InexorApplication::run() - { - spdlog::debug("Running InexorApplication."); - while(!glfwWindowShouldClose(window)) + void InexorApplication::run() { - glfwPollEvents(); - render_frame(); + spdlog::debug("Running InexorApplication."); - // TODO: Run this in a separated thread? - // TODO: Merge into one update_game_data() method? - update_cameras(); + while(!glfwWindowShouldClose(window)) + { + glfwPollEvents(); + render_frame(); - // TODO! - //update_keyboard(); - time_passed = stopwatch.get_time_step(); + // TODO: Run this in a separated thread? + // TODO: Merge into one update_game_data() method? + update_cameras(); + + // TODO! + //update_keyboard(); + time_passed = stopwatch.get_time_step(); + } } - } - void InexorApplication::cleanup() - { - spdlog::debug("Cleaning up InexorApplication."); + void InexorApplication::cleanup() + { + spdlog::debug("Cleaning up InexorApplication."); - shutdown_vulkan(); - - glfwDestroyWindow(window); - glfwTerminate(); + shutdown_vulkan(); - vertex_shader_files.clear(); - fragment_shader_files.clear(); - texture_files.clear(); - shader_files.clear(); - gltf_model_files.clear(); - } + glfwDestroyWindow(window); + glfwTerminate(); + vertex_shader_files.clear(); + fragment_shader_files.clear(); + texture_files.clear(); + shader_files.clear(); + gltf_model_files.clear(); + } -}; + + }; }; diff --git a/src/vulkan-renderer/renderer.cpp b/src/vulkan-renderer/renderer.cpp index 6eb3fdd89..f6fb487fa 100644 --- a/src/vulkan-renderer/renderer.cpp +++ b/src/vulkan-renderer/renderer.cpp @@ -1330,7 +1330,7 @@ namespace inexor // TODO: Implement -wireframe command line argument. // Because the pipeline in Vulkan is immutable, this guides us to record a second command line with wireframe enabled. - pipeline_rasterization_state_create_info.polygonMode = VK_POLYGON_MODE_LINE; + pipeline_rasterization_state_create_info.polygonMode = VK_POLYGON_MODE_FILL; pipeline_rasterization_state_create_info.cullMode = VK_CULL_MODE_BACK_BIT; pipeline_rasterization_state_create_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; pipeline_rasterization_state_create_info.depthBiasEnable = VK_FALSE;