diff --git a/data/shaders/multithreading/generate-spirv.bat b/data/shaders/multithreading/generate-spirv.bat index e9a2f883..0b4e02ac 100644 --- a/data/shaders/multithreading/generate-spirv.bat +++ b/data/shaders/multithreading/generate-spirv.bat @@ -1,2 +1,4 @@ glslangvalidator -V phong.vert -o phong.vert.spv -glslangvalidator -V phong.frag -o phong.frag.spv \ No newline at end of file +glslangvalidator -V phong.frag -o phong.frag.spv +glslangvalidator -V starsphere.vert -o starsphere.vert.spv +glslangvalidator -V starsphere.frag -o starsphere.frag.spv \ No newline at end of file diff --git a/data/shaders/multithreading/phong.vert b/data/shaders/multithreading/phong.vert index 3ce12417..ca66c224 100644 --- a/data/shaders/multithreading/phong.vert +++ b/data/shaders/multithreading/phong.vert @@ -21,7 +21,15 @@ layout (location = 4) out vec3 outLightVec; void main() { outNormal = inNormal; - outColor = inColor;// * pushConsts.color; + + if ( (inColor.r == 1.0) && (inColor.g == 0.0) && (inColor.b == 0.0)) + { + outColor = pushConsts.color; + } + else + { + outColor = inColor; + } gl_Position = pushConsts.mvp * vec4(inPos.xyz, 1.0); diff --git a/data/shaders/multithreading/phong.vert.spv b/data/shaders/multithreading/phong.vert.spv index 29c15b9c..49239829 100644 Binary files a/data/shaders/multithreading/phong.vert.spv and b/data/shaders/multithreading/phong.vert.spv differ diff --git a/data/shaders/multithreading/starsphere.frag b/data/shaders/multithreading/starsphere.frag new file mode 100644 index 00000000..07e35d3f --- /dev/null +++ b/data/shaders/multithreading/starsphere.frag @@ -0,0 +1,42 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (location = 0) in vec3 inUVW; + +layout (location = 0) out vec4 outFragColor; + +#define HASHSCALE3 vec3(443.897, 441.423, 437.195) +#define STARFREQUENCY 0.01 + +// Hash function by Dave Hoskins (https://www.shadertoy.com/view/4djSRW) +float hash33(vec3 p3) +{ + p3 = fract(p3 * HASHSCALE3); + p3 += dot(p3, p3.yxz+19.19); + return fract(vec3((p3.x + p3.y)*p3.z + (p3.x+p3.z)*p3.y + (p3.y+p3.z)*p3.x)); +} + +float starField(vec3 pos) +{ + vec3 color = vec3(0.0); + float threshhold = (1.0 - STARFREQUENCY); + float rnd = hash33(pos); + if (rnd >= threshhold) + { + float starCol = pow((rnd - threshhold) / (1.0 - threshhold), 16.0); + color += vec3(starCol); + } + return color; +} + +void main() +{ + // Fake atmosphere at the bottom + vec3 atmosphere = clamp(vec3(0.1, 0.15, 0.4) * (inUVW.t - 5.0), 0.0, 1.0); + + vec3 color = starField(inUVW) + atmosphere; + + outFragColor = vec4(color, 1.0); +} \ No newline at end of file diff --git a/data/shaders/multithreading/starsphere.frag.spv b/data/shaders/multithreading/starsphere.frag.spv new file mode 100644 index 00000000..9d590a2e Binary files /dev/null and b/data/shaders/multithreading/starsphere.frag.spv differ diff --git a/data/shaders/multithreading/starsphere.vert b/data/shaders/multithreading/starsphere.vert new file mode 100644 index 00000000..e22fbbda --- /dev/null +++ b/data/shaders/multithreading/starsphere.vert @@ -0,0 +1,19 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (location = 0) in vec3 inPos; + +layout (std140, push_constant) uniform PushConsts +{ + mat4 mvp; +} pushConsts; + +layout (location = 0) out vec3 outUVW; + +void main() +{ + outUVW = inPos; + gl_Position = pushConsts.mvp * vec4(inPos.xyz, 1.0); +} diff --git a/data/shaders/multithreading/starsphere.vert.spv b/data/shaders/multithreading/starsphere.vert.spv new file mode 100644 index 00000000..055b3352 Binary files /dev/null and b/data/shaders/multithreading/starsphere.vert.spv differ diff --git a/multithreading/multithreading.cpp b/multithreading/multithreading.cpp index 55b4079d..64108dce 100644 --- a/multithreading/multithreading.cpp +++ b/multithreading/multithreading.cpp @@ -46,6 +46,7 @@ public: struct { vkMeshLoader::MeshBuffer ufo; + vkMeshLoader::MeshBuffer skysphere; } meshes; // Shared matrices used for thread push constant blocks @@ -56,6 +57,7 @@ public: struct { VkPipeline phong; + VkPipeline starsphere; } pipelines; VkPipelineLayout pipelineLayout; @@ -63,6 +65,7 @@ public: VkDescriptorSetLayout descriptorSetLayout; VkCommandBuffer primaryCommandBuffer; + VkCommandBuffer secondaryCommandBuffer; // Number of animated objects to be renderer // by using threads and secondary command buffers @@ -86,10 +89,12 @@ public: }; struct ObjectData { + glm::mat4 model; glm::vec3 pos; glm::vec3 rotation; float rotationDir; float rotationSpeed; + float scale; float deltaT; }; @@ -120,8 +125,11 @@ public: // todo : May not work on all compilers (e.g. old GCC versions?) numThreads = std::thread::hardware_concurrency(); assert(numThreads > 0); - // todo : test, remove +#if defined(__ANDROID__) + LOGD("numThreads = %d", numThreads); +#else std::cout << "numThreads = " << numThreads << std::endl; +#endif srand(time(NULL)); threadPool.setThreadCount(numThreads); @@ -134,13 +142,16 @@ public: // Clean up used Vulkan resources // Note : Inherited destructor cleans up resources stored in base class vkDestroyPipeline(device, pipelines.phong, nullptr); + vkDestroyPipeline(device, pipelines.starsphere, nullptr); vkDestroyPipelineLayout(device, pipelineLayout, nullptr); vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); vkFreeCommandBuffers(device, cmdPool, 1, &primaryCommandBuffer); + vkFreeCommandBuffers(device, cmdPool, 1, &secondaryCommandBuffer); vkMeshLoader::freeMeshBufferResources(device, &meshes.ufo); + vkMeshLoader::freeMeshBufferResources(device, &meshes.skysphere); for (auto& thread : threadData) { @@ -157,16 +168,22 @@ public: // Create all threads and initialize shader push constants void prepareMultiThreadedRenderer() { + // todo : separate func + // Since this demo updates the command buffers on each frame // we don't use the per-framebuffer command buffers from the // base class, and create a single primary command buffer instead - VkCommandBufferAllocateInfo primaryAllocateInfo = + VkCommandBufferAllocateInfo cmdBufAllocateInfo = vkTools::initializers::commandBufferAllocateInfo( cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1); - vkTools::checkResult(vkAllocateCommandBuffers(device, &primaryAllocateInfo, &primaryCommandBuffer)); + vkTools::checkResult(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &primaryCommandBuffer)); + // Create a secondary command buffer for rendering the star sphere + cmdBufAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; + vkTools::checkResult(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &secondaryCommandBuffer)); + threadData.resize(numThreads); createSetupCommandBuffer(); @@ -188,12 +205,12 @@ public: // One secondary command buffer per object that is updated by this thread thread->commandBuffer.resize(numObjectsPerThread); // Generate secondary command buffers for each thread - VkCommandBufferAllocateInfo cmdBufAllocateInfo = + VkCommandBufferAllocateInfo secondaryCmdBufAllocateInfo = vkTools::initializers::commandBufferAllocateInfo( thread->commandPool, VK_COMMAND_BUFFER_LEVEL_SECONDARY, thread->commandBuffer.size()); - vkTools::checkResult(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, thread->commandBuffer.data())); + vkTools::checkResult(vkAllocateCommandBuffers(device, &secondaryCmdBufAllocateInfo, thread->commandBuffer.data())); // Unique vertex and index buffers per thread @@ -250,12 +267,12 @@ public: posZ += 1.0f; } - thread->objectData[j].rotation = glm::vec3(0.0f, (float)(rand() % 360), 0.0f); + thread->objectData[j].rotation = glm::vec3(0.0f, rnd(360.0f), 0.0f); thread->objectData[j].deltaT = rnd(1.0f); thread->objectData[j].rotationDir = (rnd(100.0f) < 50.0f) ? 1.0f : -1.0f; - thread->objectData[i].rotationSpeed = (2.0f + rnd(4.0f)) * thread->objectData[i].rotationDir; + thread->objectData[j].rotationSpeed = (2.0f + rnd(4.0f)) * thread->objectData[i].rotationDir; + thread->objectData[j].scale = 0.75f + rnd(0.5f); - // Random object color thread->pushConstBlock[j].color = glm::vec3(rnd(1.0f), rnd(1.0f), rnd(1.0f)); } } @@ -293,17 +310,18 @@ public: { objectData->rotation.y -= 360.0f; } - objectData->deltaT += 0.0005f; + objectData->deltaT += 0.0015f; if (objectData->deltaT > 1.0f) objectData->deltaT -= 1.0f; objectData->pos.y = sin(glm::radians(objectData->deltaT * 360.0f)) * 1.5f; - glm::mat4 model = glm::translate(glm::mat4(), objectData->pos); - model = glm::rotate(model, -sinf(glm::radians(objectData->deltaT * 360.0f)) * 0.25f, glm::vec3(objectData->rotationDir, 0.0f, 0.0f)); - model = glm::rotate(model, glm::radians(objectData->rotation.y), glm::vec3(0.0f, objectData->rotationDir, 0.0f)); - model = glm::rotate(model, glm::radians(objectData->deltaT * 360.0f), glm::vec3(0.0f, objectData->rotationDir, 0.0f)); + objectData->model = glm::translate(glm::mat4(), objectData->pos); + objectData->model = glm::rotate(objectData->model, -sinf(glm::radians(objectData->deltaT * 360.0f)) * 0.25f, glm::vec3(objectData->rotationDir, 0.0f, 0.0f)); + objectData->model = glm::rotate(objectData->model, glm::radians(objectData->rotation.y), glm::vec3(0.0f, objectData->rotationDir, 0.0f)); + objectData->model = glm::rotate(objectData->model, glm::radians(objectData->deltaT * 360.0f), glm::vec3(0.0f, objectData->rotationDir, 0.0f)); + objectData->model = glm::scale(objectData->model, glm::vec3(objectData->scale)); - thread->pushConstBlock[cmdBufferIndex].mvp = matrices.projection * matrices.view * model; + thread->pushConstBlock[cmdBufferIndex].mvp = matrices.projection * matrices.view * objectData->model; // Update shader push constant block // Contains model view matrix @@ -323,6 +341,47 @@ public: vkTools::checkResult(vkEndCommandBuffer(cmdBuffer)); } + void updateSecondaryCommandBuffer(VkCommandBufferInheritanceInfo inheritanceInfo) + { + // Secondary command buffer for the sky sphere + VkCommandBufferBeginInfo commandBufferBeginInfo = vkTools::initializers::commandBufferBeginInfo(); + commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; + commandBufferBeginInfo.pInheritanceInfo = &inheritanceInfo; + + vkTools::checkResult(vkBeginCommandBuffer(secondaryCommandBuffer, &commandBufferBeginInfo)); + + VkViewport viewport = vkTools::initializers::viewport((float)width, (float)height, 0.0f, 1.0f); + vkCmdSetViewport(secondaryCommandBuffer, 0, 1, &viewport); + + VkRect2D scissor = vkTools::initializers::rect2D(width, height, 0, 0); + vkCmdSetScissor(secondaryCommandBuffer, 0, 1, &scissor); + + vkCmdBindPipeline(secondaryCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.starsphere); + + + glm::mat4 view = glm::mat4(); + view = glm::rotate(view, glm::radians(rotation.x), glm::vec3(1.0f, 0.0f, 0.0f)); + view = glm::rotate(view, glm::radians(rotation.y), glm::vec3(0.0f, 1.0f, 0.0f)); + view = glm::rotate(view, glm::radians(rotation.z), glm::vec3(0.0f, 0.0f, 1.0f)); + + glm::mat4 mvp = matrices.projection * view; + + vkCmdPushConstants( + secondaryCommandBuffer, + pipelineLayout, + VK_SHADER_STAGE_VERTEX_BIT, + 0, + sizeof(mvp), + &mvp); + + VkDeviceSize offsets[1] = { 0 }; + vkCmdBindVertexBuffers(secondaryCommandBuffer, 0, 1, &meshes.skysphere.vertices.buf, offsets); + vkCmdBindIndexBuffer(secondaryCommandBuffer, meshes.skysphere.indices.buf, 0, VK_INDEX_TYPE_UINT32); + vkCmdDrawIndexed(secondaryCommandBuffer, meshes.skysphere.indexCount, 1, 0, 0, 0); + + vkTools::checkResult(vkEndCommandBuffer(secondaryCommandBuffer)); + } + // Updates the secondary command buffers using a thread pool // and puts them into the primary command buffer that's // lat submitted to the queue for rendering @@ -353,14 +412,19 @@ public: // These are stored (and retrieved) from the secondary command buffers vkCmdBeginRenderPass(primaryCommandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); - std::vector commandBuffers; - // Inheritance info for the secondary command buffers VkCommandBufferInheritanceInfo inheritanceInfo = vkTools::initializers::commandBufferInheritanceInfo(); inheritanceInfo.renderPass = renderPass; // Secondary command buffer also use the currently active framebuffer inheritanceInfo.framebuffer = frameBuffer; + // Contains the list of secondary command buffers to be executed + std::vector commandBuffers; + + // Secondary command buffer with star background sphere + updateSecondaryCommandBuffer(inheritanceInfo); + commandBuffers.push_back(secondaryCommandBuffer); + for (uint32_t t = 0; t < numThreads; t++) { // Add a job to the thread's queue for each object to be rendered @@ -422,6 +486,7 @@ public: void loadMeshes() { loadMesh("./../data/models/retroufo_red.X", &meshes.ufo, vertexLayout, 0.12f); + loadMesh("./../data/models/sphere.obj", &meshes.skysphere, vertexLayout, 1.0f); } void setupVertexDescriptions() @@ -589,8 +654,8 @@ public: // Load shaders std::array shaderStages; - shaderStages[0] = loadShader("./../data/shaders/multithreading/phong.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); - shaderStages[1] = loadShader("./../data/shaders/multithreading/phong.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + shaderStages[0] = loadShader(getAssetPath() + "shaders/multithreading/phong.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getAssetPath() + "shaders/multithreading/phong.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); VkGraphicsPipelineCreateInfo pipelineCreateInfo = vkTools::initializers::pipelineCreateInfo( @@ -609,8 +674,14 @@ public: pipelineCreateInfo.stageCount = shaderStages.size(); pipelineCreateInfo.pStages = shaderStages.data(); - VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.phong); - assert(!err); + vkTools::checkResult(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.phong)); + + // Star sphere rendering pipeline + rasterizationState.cullMode = VK_CULL_MODE_FRONT_BIT; + depthStencilState.depthWriteEnable = VK_FALSE; + shaderStages[0] = loadShader(getAssetPath() + "shaders/multithreading/starsphere.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getAssetPath() + "shaders/multithreading/starsphere.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + vkTools::checkResult(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.starsphere)); } void updateMatrices()