PRAGMA_WARNING_POP //[-------------------------------------------------------] //[ Public virtual IApplication methods ] //[-------------------------------------------------------] void VertexBuffer::onInitialization() { // Get and check the renderer instance Renderer::IRendererPtr renderer(getRenderer()); if (nullptr != renderer) { // Create the buffer manager mBufferManager = renderer->createBufferManager(); { // Create the root signature // Setup Renderer::RootSignatureBuilder rootSignature; rootSignature.initialize(0, nullptr, 0, nullptr, Renderer::RootSignatureFlags::ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); // Create the instance mRootSignature = renderer->createRootSignature(rootSignature); } // Vertex input layout static constexpr Renderer::VertexAttribute vertexAttributesLayoutVBO[] = { { // Attribute 0 // Data destination Renderer::VertexAttributeFormat::FLOAT_2, // vertexAttributeFormat (Renderer::VertexAttributeFormat) "Position", // name[32] (char) "POSITION", // semanticName[32] (char) 0, // semanticIndex (uint32_t) // Data source 0, // inputSlot (uint32_t) 0, // alignedByteOffset (uint32_t) sizeof(float) * 5, // strideInBytes (uint32_t) 0 // instancesPerElement (uint32_t) }, { // Attribute 1 // Data destination Renderer::VertexAttributeFormat::FLOAT_3, // vertexAttributeFormat (Renderer::VertexAttributeFormat) "Color", // name[32] (char) "COLOR", // semanticName[32] (char) 0, // semanticIndex (uint32_t) // Data source 0, // inputSlot (uint32_t) sizeof(float) * 2, // alignedByteOffset (uint32_t) sizeof(float) * 5, // strideInBytes (uint32_t) 0 // instancesPerElement (uint32_t) } }; const Renderer::VertexAttributes vertexAttributesVBO(static_cast<uint32_t>(GLM_COUNTOF(vertexAttributesLayoutVBO)), vertexAttributesLayoutVBO); static constexpr Renderer::VertexAttribute vertexAttributesLayoutVBOs[] = { { // Attribute 0 // Data destination Renderer::VertexAttributeFormat::FLOAT_2, // vertexAttributeFormat (Renderer::VertexAttributeFormat) "Position", // name[32] (char) "POSITION", // semanticName[32] (char) 0, // semanticIndex (uint32_t) // Data source 0, // inputSlot (uint32_t) 0, // alignedByteOffset (uint32_t) sizeof(float) * 2, // strideInBytes (uint32_t) 0 // instancesPerElement (uint32_t) }, { // Attribute 1 // Data destination Renderer::VertexAttributeFormat::FLOAT_3, // vertexAttributeFormat (Renderer::VertexAttributeFormat) "Color", // name[32] (char) "COLOR", // semanticName[32] (char) 0, // semanticIndex (uint32_t) // Data source 1, // inputSlot (uint32_t) 0, // alignedByteOffset (uint32_t) sizeof(float) * 3, // strideInBytes (uint32_t) 0 // instancesPerElement (uint32_t) } }; const Renderer::VertexAttributes vertexAttributesVBOs(static_cast<uint32_t>(GLM_COUNTOF(vertexAttributesLayoutVBOs)), vertexAttributesLayoutVBOs); // Vertex array object (VAO) // -> The vertex array object (VAO) keeps a reference to the used vertex buffer object (VBO) // -> This means that there's no need to keep an own vertex buffer object (VBO) reference // -> When the vertex array object (VAO) is destroyed, it automatically decreases the // reference of the used vertex buffer objects (VBO). If the reference counter of a // vertex buffer object (VBO) reaches zero, it's automatically destroyed. { // Create vertex array object (VAO) // Create the vertex buffer object (VBO) holding position and color data // -> Clip space vertex positions, left/bottom is (-1,-1) and right/top is (1,1) // -> Traditional normalized RGB vertex colors static constexpr float VERTEX_POSITION_COLOR[] = { // Position Color // Vertex ID Triangle on screen 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, // 0 0 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // 1 . . -0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 2 2.......1 }; Renderer::IVertexBufferPtr vertexBufferPositionColor(mBufferManager->createVertexBuffer(sizeof(VERTEX_POSITION_COLOR), VERTEX_POSITION_COLOR)); // Create vertex array object (VAO) const Renderer::VertexArrayVertexBuffer vertexArrayVertexBuffers[] = { vertexBufferPositionColor }; mVertexArrayVBO = mBufferManager->createVertexArray(vertexAttributesVBO, static_cast<uint32_t>(GLM_COUNTOF(vertexArrayVertexBuffers)), vertexArrayVertexBuffers); } { // Create vertex array object (VAO) using multiple vertex buffer object (VBO) // Create the vertex buffer object (VBO) holding color data // -> Traditional normalized RGB vertex colors static constexpr float VERTEX_COLOR[] = { // Vertex ID Triangle on screen 1.0f, 0.0f, 0.0f, // 0 0.......1 0.0f, 1.0f, 0.0f, // 1 . . 0.0f, 0.0f, 1.0f // 2 2 }; Renderer::IVertexBufferPtr vertexBufferColor(mBufferManager->createVertexBuffer(sizeof(VERTEX_COLOR), VERTEX_COLOR)); // Create the vertex buffer object (VBO) holding position data // -> Clip space vertex positions, left/bottom is (-1,-1) and right/top is (1,1) static constexpr float VERTEX_POSITION[] = { // Vertex ID Triangle on screen -0.5f, 0.0f, // 0 0.......1 1.0f, 0.0f, // 1 . . 0.0f, -1.0f // 2 2 }; Renderer::IVertexBufferPtr vertexBufferPosition(mBufferManager->createVertexBuffer(sizeof(VERTEX_POSITION), VERTEX_POSITION)); // Create vertex array object (VAO) const Renderer::VertexArrayVertexBuffer vertexArrayVertexBuffers[] = { vertexBufferPosition, vertexBufferColor }; mVertexArrayVBOs = mBufferManager->createVertexArray(vertexAttributesVBOs, static_cast<uint32_t>(GLM_COUNTOF(vertexArrayVertexBuffers)), vertexArrayVertexBuffers); } { // Get the shader source code (outsourced to keep an overview) const char* vertexShaderSourceCode = nullptr; const char* fragmentShaderSourceCode = nullptr; #include "VertexBuffer_GLSL_450.h" // For Vulkan #include "VertexBuffer_GLSL_410.h" // macOS 10.11 only supports OpenGL 4.1 hence it's our OpenGL minimum #include "VertexBuffer_GLSL_ES3.h" #include "VertexBuffer_HLSL_D3D9_D3D10_D3D11_D3D12.h" #include "VertexBuffer_Null.h" Renderer::IShaderLanguage& shaderLanguage = renderer->getDefaultShaderLanguage(); { // Create pipeline state objects (PSO) using one vertex buffer object (VBO) // Create the graphics program Renderer::IGraphicsProgramPtr graphicsProgram; graphicsProgram = shaderLanguage.createGraphicsProgram( *mRootSignature, vertexAttributesVBO, shaderLanguage.createVertexShaderFromSourceCode(vertexAttributesVBO, vertexShaderSourceCode), shaderLanguage.createFragmentShaderFromSourceCode(fragmentShaderSourceCode)); // Create the graphics pipeline state objects (PSO) if (nullptr != graphicsProgram) { mGraphicsPipelineStateVBO = renderer->createGraphicsPipelineState(Renderer::GraphicsPipelineStateBuilder(mRootSignature, graphicsProgram, vertexAttributesVBO, getMainRenderTarget()->getRenderPass())); } } { // Create graphics pipeline state objects (PSO) using multiple vertex buffer object (VBO) // Create the graphics program Renderer::IGraphicsProgramPtr graphicsProgram; graphicsProgram = shaderLanguage.createGraphicsProgram( *mRootSignature, vertexAttributesVBOs, shaderLanguage.createVertexShaderFromSourceCode(vertexAttributesVBOs, vertexShaderSourceCode), shaderLanguage.createFragmentShaderFromSourceCode(fragmentShaderSourceCode)); // Create the graphics pipeline state objects (PSO) if (nullptr != graphicsProgram) { mGraphicsPipelineStateVBOs = renderer->createGraphicsPipelineState(Renderer::GraphicsPipelineStateBuilder(mRootSignature, graphicsProgram, vertexAttributesVBOs, getMainRenderTarget()->getRenderPass())); } } } // Since we're always submitting the same commands to the renderer, we can fill the command buffer once during initialization and then reuse it multiple times during runtime fillCommandBuffer(); } }
//[-------------------------------------------------------] //[ Public virtual IApplication methods ] //[-------------------------------------------------------] void FirstGeometryShader::onInitialization() { // Call the base implementation IApplicationRenderer::onInitialization(); // Get and check the renderer instance // -> Geometry shaders supported? Renderer::IRendererPtr renderer(getRenderer()); if (nullptr != renderer && renderer->getCapabilities().maximumNumberOfGsOutputVertices) { // Create the buffer manager mBufferManager = renderer->createBufferManager(); { // Create the root signature // Setup Renderer::RootSignatureBuilder rootSignature; rootSignature.initialize(0, nullptr, 0, nullptr, Renderer::RootSignatureFlags::ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); // Create the instance mRootSignature = renderer->createRootSignature(rootSignature); } // Vertex input layout const Renderer::VertexAttribute vertexAttributesLayout[] = { { // Attribute 0 // Data destination Renderer::VertexAttributeFormat::FLOAT_1, // vertexAttributeFormat (Renderer::VertexAttributeFormat) "Position", // name[32] (char) "POSITION", // semanticName[32] (char) 0, // semanticIndex (uint32_t) // Data source 0, // inputSlot (uint32_t) 0, // alignedByteOffset (uint32_t) // Data source, instancing part 0 // instancesPerElement (uint32_t) } }; const Renderer::VertexAttributes vertexAttributes(glm::countof(vertexAttributesLayout), vertexAttributesLayout); { // Create vertex array object (VAO) // Create the vertex buffer object (VBO) static const float VERTEX_POSITION[] = { // Vertex ID 42.0f // 0 }; Renderer::IVertexBufferPtr vertexBuffer(mBufferManager->createVertexBuffer(sizeof(VERTEX_POSITION), VERTEX_POSITION, Renderer::BufferUsage::STATIC_DRAW)); // Create vertex array object (VAO) // -> The vertex array object (VAO) keeps a reference to the used vertex buffer object (VBO) // -> This means that there's no need to keep an own vertex buffer object (VBO) reference // -> When the vertex array object (VAO) is destroyed, it automatically decreases the // reference of the used vertex buffer objects (VBO). If the reference counter of a // vertex buffer object (VBO) reaches zero, it's automatically destroyed. const Renderer::VertexArrayVertexBuffer vertexArrayVertexBuffers[] = { { // Vertex buffer 0 vertexBuffer, // vertexBuffer (Renderer::IVertexBuffer *) sizeof(float) // strideInBytes (uint32_t) } }; mVertexArray = mBufferManager->createVertexArray(vertexAttributes, glm::countof(vertexArrayVertexBuffers), vertexArrayVertexBuffers); } // Create the program: Decide which shader language should be used (for example "GLSL" or "HLSL") Renderer::IShaderLanguagePtr shaderLanguage(renderer->getShaderLanguage()); if (nullptr != shaderLanguage) { // Create the program Renderer::IProgramPtr program; { // Get the shader source code (outsourced to keep an overview) const char *vertexShaderSourceCode = nullptr; const char *geometryShaderSourceCode = nullptr; const char *fragmentShaderSourceCode = nullptr; #include "FirstGeometryShader_GLSL_410.h" #include "FirstGeometryShader_HLSL_D3D10_D3D11_D3D12.h" #include "FirstGeometryShader_Null.h" // Create the program program = shaderLanguage->createProgram( *mRootSignature, vertexAttributes, shaderLanguage->createVertexShaderFromSourceCode(vertexAttributes, vertexShaderSourceCode), shaderLanguage->createGeometryShaderFromSourceCode(geometryShaderSourceCode, Renderer::GsInputPrimitiveTopology::POINTS, Renderer::GsOutputPrimitiveTopology::TRIANGLE_STRIP, 3), shaderLanguage->createFragmentShaderFromSourceCode(fragmentShaderSourceCode)); } // Create the pipeline state object (PSO) if (nullptr != program) { Renderer::PipelineState pipelineState = Renderer::PipelineStateBuilder(mRootSignature, program, vertexAttributes); pipelineState.primitiveTopologyType = Renderer::PrimitiveTopologyType::POINT; mPipelineState = renderer->createPipelineState(pipelineState); } } // Since we're always submitting the same commands to the renderer, we can fill the command buffer once during initialization and then reuse it multiple times during runtime fillCommandBuffer(); } }
//[-------------------------------------------------------] //[ Public virtual IApplication methods ] //[-------------------------------------------------------] void FirstMesh::onInitialization() { // Call the base implementation IApplicationRendererRuntime::onInitialization(); // Get and check the renderer runtime instance RendererRuntime::IRendererRuntime* rendererRuntime = getRendererRuntime(); if (nullptr != rendererRuntime) { Renderer::IRendererPtr renderer(getRenderer()); // Create uniform buffers // -> Direct3D 9 and OpenGL ES 2 do not support uniform buffers // -> Direct3D 10, 11 and 12 do not support individual uniforms // -> The renderer is just a light weight abstraction layer, so we need to handle the differences if ((0 == strcmp(renderer->getName(), "Direct3D10") || 0 == strcmp(renderer->getName(), "Direct3D11") || 0 == strcmp(renderer->getName(), "Direct3D12"))) { // Allocate enough memory for two 4x4 floating point matrices mUniformBuffer = rendererRuntime->getBufferManager().createUniformBuffer(2 * 4 * 4 * sizeof(float), nullptr, Renderer::BufferUsage::DYNAMIC_DRAW); } // Decide which shader language should be used (for example "GLSL" or "HLSL") Renderer::IShaderLanguagePtr shaderLanguage(renderer->getShaderLanguage()); if (nullptr != shaderLanguage) { // Vertex input layout const Renderer::VertexAttribute vertexAttributesLayout[] = { { // Attribute 0 // Data destination Renderer::VertexAttributeFormat::FLOAT_3, // vertexAttributeFormat (Renderer::VertexAttributeFormat) "Position", // name[32] (char) "POSITION", // semanticName[32] (char) 0, // semanticIndex (uint32_t) // Data source 0, // inputSlot (uint32_t) 0, // alignedByteOffset (uint32_t) // Data source, instancing part 0 // instancesPerElement (uint32_t) }, { // Attribute 1 // Data destination Renderer::VertexAttributeFormat::SHORT_2, // vertexAttributeFormat (Renderer::VertexAttributeFormat) "TexCoord", // name[32] (char) "TEXCOORD", // semanticName[32] (char) 0, // semanticIndex (uint32_t) // Data source 0, // inputSlot (uint32_t) sizeof(float) * 3, // alignedByteOffset (uint32_t) // Data source, instancing part 0 // instancesPerElement (uint32_t) }, { // Attribute 2 // Data destination Renderer::VertexAttributeFormat::SHORT_4, // vertexAttributeFormat (Renderer::VertexAttributeFormat) "QTangent", // name[32] (char) "TEXCOORD", // semanticName[32] (char) 1, // semanticIndex (uint32_t) // Data source 0, // inputSlot (uint32_t) sizeof(float) * 3 + sizeof(short) * 2, // alignedByteOffset (uint32_t) // Data source, instancing part 0 // instancesPerElement (uint32_t) } }; const Renderer::VertexAttributes vertexAttributes(glm::countof(vertexAttributesLayout), vertexAttributesLayout); { // Create the root signature Renderer::DescriptorRangeBuilder ranges[6]; ranges[0].initialize(Renderer::DescriptorRangeType::UBV, 1, 0, "UniformBlockDynamicVs", 0); ranges[1].initializeSampler(1, 0); ranges[2].initialize(Renderer::DescriptorRangeType::SRV, 1, 0, "DiffuseMap", 1); ranges[3].initialize(Renderer::DescriptorRangeType::SRV, 1, 1, "EmissiveMap", 1); ranges[4].initialize(Renderer::DescriptorRangeType::SRV, 1, 2, "NormalMap", 1); ranges[5].initialize(Renderer::DescriptorRangeType::SRV, 1, 3, "SpecularMap", 1); Renderer::RootParameterBuilder rootParameters[6]; rootParameters[0].initializeAsDescriptorTable(1, &ranges[0], Renderer::ShaderVisibility::VERTEX); rootParameters[1].initializeAsDescriptorTable(1, &ranges[1], Renderer::ShaderVisibility::FRAGMENT); rootParameters[2].initializeAsDescriptorTable(1, &ranges[2], Renderer::ShaderVisibility::FRAGMENT); rootParameters[3].initializeAsDescriptorTable(1, &ranges[3], Renderer::ShaderVisibility::FRAGMENT); rootParameters[4].initializeAsDescriptorTable(1, &ranges[4], Renderer::ShaderVisibility::FRAGMENT); rootParameters[5].initializeAsDescriptorTable(1, &ranges[5], Renderer::ShaderVisibility::FRAGMENT); // Setup Renderer::RootSignatureBuilder rootSignature; rootSignature.initialize(glm::countof(rootParameters), rootParameters, 0, nullptr, Renderer::RootSignatureFlags::ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); // Create the instance mRootSignature = renderer->createRootSignature(rootSignature); } // Create the program Renderer::IProgramPtr program; { // Get the shader source code (outsourced to keep an overview) const char *vertexShaderProfile = nullptr; const char *vertexShaderSourceCode = nullptr; const char *fragmentShaderProfile = nullptr; const char *fragmentShaderSourceCode = nullptr; #include "FirstMesh_GLSL_410.h" #include "FirstMesh_GLSL_ES2.h" #include "FirstMesh_HLSL_D3D9.h" #include "FirstMesh_HLSL_D3D10_D3D11_D3D12.h" #include "FirstMesh_Null.h" // Create the program mProgram = program = shaderLanguage->createProgram( *mRootSignature, vertexAttributes, shaderLanguage->createVertexShaderFromSourceCode(vertexAttributes, vertexShaderSourceCode, vertexShaderProfile), shaderLanguage->createFragmentShaderFromSourceCode(fragmentShaderSourceCode, fragmentShaderProfile)); } // Is there a valid program? if (nullptr != program) { // Create the pipeline state object (PSO) mPipelineState = renderer->createPipelineState(Renderer::PipelineStateBuilder(mRootSignature, program, vertexAttributes)); // Optimization: Cached data to not bother the renderer API too much if (nullptr == mUniformBuffer) { mObjectSpaceToClipSpaceMatrixUniformHandle = program->getUniformHandle("ObjectSpaceToClipSpaceMatrix"); mObjectSpaceToViewSpaceMatrixUniformHandle = program->getUniformHandle("ObjectSpaceToViewSpaceMatrix"); } } // Create mesh instance mMeshResourceId = rendererRuntime->getMeshResourceManager().loadMeshResourceByAssetId("Example/Mesh/Character/Imrod"); { // Load in the diffuse, emissive, normal and specular texture // -> The tangent space normal map is stored with three components, two would be enough to recalculate the third component within the fragment shader // -> The specular map could be put into the alpha channel of the diffuse map instead of storing it as an individual texture RendererRuntime::TextureResourceManager& textureResourceManager = rendererRuntime->getTextureResourceManager(); mDiffuseTextureResourceId = textureResourceManager.loadTextureResourceByAssetId("Example/Texture/Character/ImrodDiffuseMap"); mNormalTextureResourceId = textureResourceManager.loadTextureResourceByAssetId("Example/Texture/Character/ImrodEmissiveMap"); mSpecularTextureResourceId = textureResourceManager.loadTextureResourceByAssetId("Example/Texture/Character/ImrodNormalMap"); mEmissiveTextureResourceId = textureResourceManager.loadTextureResourceByAssetId("Example/Texture/Character/ImrodSpecularMap"); } { // Create sampler state Renderer::SamplerState samplerStateSettings = Renderer::ISamplerState::getDefaultSamplerState(); samplerStateSettings.addressU = Renderer::TextureAddressMode::WRAP; samplerStateSettings.addressV = Renderer::TextureAddressMode::WRAP; mSamplerState = renderer->createSamplerState(samplerStateSettings); } } } }
PRAGMA_WARNING_POP //[-------------------------------------------------------] //[ Public virtual IApplication methods ] //[-------------------------------------------------------] void FirstComputeShader::onInitialization() { // Get and check the renderer instance Renderer::IRendererPtr renderer(getRenderer()); if (nullptr != renderer) { // Create the buffer and texture manager mBufferManager = renderer->createBufferManager(); mTextureManager = renderer->createTextureManager(); { // Create the graphics root signature // TODO(co) Compute shader: Get rid of the OpenGL/Direct3D 11 variation here const uint32_t offset = (renderer->getNameId() == Renderer::NameId::VULKAN || renderer->getNameId() == Renderer::NameId::OPENGL) ? 1u : 0u; Renderer::DescriptorRangeBuilder ranges[5]; ranges[0].initialize(Renderer::ResourceType::UNIFORM_BUFFER, 0, "UniformBuffer", Renderer::ShaderVisibility::FRAGMENT); ranges[1].initialize(Renderer::ResourceType::TEXTURE_BUFFER, 0, "InputTextureBuffer", Renderer::ShaderVisibility::VERTEX); ranges[2].initialize(Renderer::ResourceType::STRUCTURED_BUFFER, 1u + offset, "InputStructuredBuffer", Renderer::ShaderVisibility::VERTEX); ranges[3].initialize(Renderer::ResourceType::TEXTURE_2D, 1, "AlbedoMap", Renderer::ShaderVisibility::FRAGMENT); ranges[4].initializeSampler(0, Renderer::ShaderVisibility::FRAGMENT); Renderer::RootParameterBuilder rootParameters[2]; rootParameters[0].initializeAsDescriptorTable(4, &ranges[0]); rootParameters[1].initializeAsDescriptorTable(1, &ranges[4]); // Setup Renderer::RootSignatureBuilder rootSignature; rootSignature.initialize(static_cast<uint32_t>(GLM_COUNTOF(rootParameters)), rootParameters, 0, nullptr, Renderer::RootSignatureFlags::ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); // Create the instance mGraphicsRootSignature = renderer->createRootSignature(rootSignature); } { // Create the first compute root signature Renderer::DescriptorRangeBuilder ranges[7]; // Input ranges[0].initialize(Renderer::ResourceType::TEXTURE_2D, 0, "InputTexture2D", Renderer::ShaderVisibility::COMPUTE); ranges[1].initialize(Renderer::ResourceType::INDEX_BUFFER, 1, "InputIndexBuffer", Renderer::ShaderVisibility::COMPUTE); ranges[2].initialize(Renderer::ResourceType::VERTEX_BUFFER, 2, "InputVertexBuffer", Renderer::ShaderVisibility::COMPUTE); ranges[3].initialize(Renderer::ResourceType::UNIFORM_BUFFER, 0, "InputUniformBuffer", Renderer::ShaderVisibility::COMPUTE); // Output // TODO(co) Compute shader: Get rid of the OpenGL/Direct3D 11 variation here const uint32_t offset = (renderer->getNameId() == Renderer::NameId::VULKAN || renderer->getNameId() == Renderer::NameId::OPENGL) ? 4u : 0u; ranges[4].initialize(Renderer::ResourceType::TEXTURE_2D, 0u + offset, "OutputTexture2D", Renderer::ShaderVisibility::COMPUTE, Renderer::DescriptorRangeType::UAV); ranges[5].initialize(Renderer::ResourceType::INDEX_BUFFER, 1u + offset, "OutputIndexBuffer", Renderer::ShaderVisibility::COMPUTE, Renderer::DescriptorRangeType::UAV); ranges[6].initialize(Renderer::ResourceType::VERTEX_BUFFER, 2u + offset, "OutputVertexBuffer", Renderer::ShaderVisibility::COMPUTE, Renderer::DescriptorRangeType::UAV); Renderer::RootParameterBuilder rootParameters[1]; rootParameters[0].initializeAsDescriptorTable(7, &ranges[0]); // Setup Renderer::RootSignatureBuilder rootSignature; rootSignature.initialize(static_cast<uint32_t>(GLM_COUNTOF(rootParameters)), rootParameters, 0, nullptr, Renderer::RootSignatureFlags::NONE); // Create the instance mComputeRootSignature1 = renderer->createRootSignature(rootSignature); } { // Create the second compute root signature Renderer::DescriptorRangeBuilder ranges[6]; // Input ranges[0].initialize(Renderer::ResourceType::TEXTURE_BUFFER, 0, "InputTextureBuffer", Renderer::ShaderVisibility::COMPUTE); ranges[1].initialize(Renderer::ResourceType::STRUCTURED_BUFFER, 1, "InputStructuredBuffer", Renderer::ShaderVisibility::COMPUTE); ranges[2].initialize(Renderer::ResourceType::INDIRECT_BUFFER, 2, "InputIndirectBuffer", Renderer::ShaderVisibility::COMPUTE); // Output // TODO(co) Compute shader: Get rid of the OpenGL/Direct3D 11 variation here const uint32_t offset = (renderer->getNameId() == Renderer::NameId::VULKAN || renderer->getNameId() == Renderer::NameId::OPENGL) ? 3u : 0u; ranges[3].initialize(Renderer::ResourceType::TEXTURE_BUFFER, 0u + offset, "OutputTextureBuffer", Renderer::ShaderVisibility::COMPUTE, Renderer::DescriptorRangeType::UAV); ranges[4].initialize(Renderer::ResourceType::STRUCTURED_BUFFER, 1u + offset, "OutputStructuredBuffer", Renderer::ShaderVisibility::COMPUTE, Renderer::DescriptorRangeType::UAV); ranges[5].initialize(Renderer::ResourceType::INDIRECT_BUFFER, 2u + offset, "OutputIndirectBuffer", Renderer::ShaderVisibility::COMPUTE, Renderer::DescriptorRangeType::UAV); Renderer::RootParameterBuilder rootParameters[1]; rootParameters[0].initializeAsDescriptorTable(6, &ranges[0]); // Setup Renderer::RootSignatureBuilder rootSignature; rootSignature.initialize(static_cast<uint32_t>(GLM_COUNTOF(rootParameters)), rootParameters, 0, nullptr, Renderer::RootSignatureFlags::NONE); // Create the instance mComputeRootSignature2 = renderer->createRootSignature(rootSignature); } // Create sampler state and wrap it into a resource group instance Renderer::IResource* samplerStateResource = nullptr; { Renderer::SamplerState samplerState = Renderer::ISamplerState::getDefaultSamplerState(); samplerState.maxLOD = 0.0f; samplerStateResource = renderer->createSamplerState(samplerState); mGraphicsSamplerStateGroup = mGraphicsRootSignature->createResourceGroup(1, 1, &samplerStateResource); } { // Texture buffer static constexpr float VERTEX_POSITION_OFFSET[] = { // Vertex ID Triangle on screen 0.5f, -0.5f, 0.0f, 0.0f, // 0 0 0.5f, -0.5f, 0.0f, 0.0f, // 1 . . 0.5f, -0.5f, 0.0f, 0.0f, // 2 2.......1 }; // Create the texture buffer which will be read by a compute shader mComputeInputTextureBuffer = mBufferManager->createTextureBuffer(sizeof(VERTEX_POSITION_OFFSET), VERTEX_POSITION_OFFSET); // Create the texture buffer which will be filled by a compute shader mComputeOutputTextureBuffer = mBufferManager->createTextureBuffer(sizeof(VERTEX_POSITION_OFFSET), nullptr, Renderer::BufferFlag::UNORDERED_ACCESS | Renderer::BufferFlag::SHADER_RESOURCE); } { // Structured buffer struct Vertex { float position[2]; float padding[2]; }; static constexpr Vertex VERTICES[] = { // Vertex ID Triangle on screen { -0.5f, 0.5f, 0.0f, 0.0f }, // 0 0 { -0.5f, 0.5f, 0.0f, 0.0f }, // 1 . . { -0.5f, 0.5f, 0.0f, 0.0f }, // 2 2.......1 }; // Create the structured buffer which will be read by a compute shader mComputeInputStructuredBuffer = mBufferManager->createStructuredBuffer(sizeof(VERTICES), VERTICES, Renderer::BufferFlag::SHADER_RESOURCE, Renderer::BufferUsage::STATIC_DRAW, sizeof(Vertex)); // Create the structured buffer which will be filled by a compute shader mComputeOutputStructuredBuffer = mBufferManager->createStructuredBuffer(sizeof(VERTICES), nullptr, Renderer::BufferFlag::UNORDERED_ACCESS | Renderer::BufferFlag::SHADER_RESOURCE, Renderer::BufferUsage::STATIC_DRAW, sizeof(Vertex)); } { // Indirect buffer { // Create the indirect buffer which will be read by a compute shader const Renderer::DrawIndexedArguments drawIndexedArguments = { 0, // indexCountPerInstance (uint32_t) - Filled by compute shader via atomics counting 1, // instanceCount (uint32_t) 0, // startIndexLocation (uint32_t) 0, // baseVertexLocation (int32_t) 0 // startInstanceLocation (uint32_t) }; mComputeInputIndirectBuffer = mBufferManager->createIndirectBuffer(sizeof(Renderer::DrawIndexedArguments), &drawIndexedArguments, Renderer::IndirectBufferFlag::SHADER_RESOURCE | Renderer::IndirectBufferFlag::DRAW_INDEXED_ARGUMENTS); } // Create the indirect buffer which will be filled by a compute shader mComputeOutputIndirectBuffer = mBufferManager->createIndirectBuffer(sizeof(Renderer::DrawIndexedArguments), nullptr, Renderer::IndirectBufferFlag::UNORDERED_ACCESS | Renderer::IndirectBufferFlag::DRAW_INDEXED_ARGUMENTS); } // Vertex input layout static constexpr Renderer::VertexAttribute vertexAttributesLayout[] = { { // Attribute 0 // Data destination Renderer::VertexAttributeFormat::FLOAT_2, // vertexAttributeFormat (Renderer::VertexAttributeFormat) "Position", // name[32] (char) "POSITION", // semanticName[32] (char) 0, // semanticIndex (uint32_t) // Data source 0, // inputSlot (uint32_t) 0, // alignedByteOffset (uint32_t) sizeof(float) * 2, // strideInBytes (uint32_t) 0 // instancesPerElement (uint32_t) } }; const Renderer::VertexAttributes vertexAttributes(static_cast<uint32_t>(GLM_COUNTOF(vertexAttributesLayout)), vertexAttributesLayout); { // Create the index buffer object (IBO) static constexpr uint16_t INDICES[] = { 0, 1, 2 }; mComputeInputIndexBuffer = mBufferManager->createIndexBuffer(sizeof(INDICES), INDICES, Renderer::BufferFlag::SHADER_RESOURCE); mComputeOutputIndexBuffer = mBufferManager->createIndexBuffer(sizeof(INDICES), nullptr, Renderer::BufferFlag::UNORDERED_ACCESS); } { // Create vertex array object (VAO) // Create the vertex buffer object (VBO) // -> Clip space vertex positions, left/bottom is (-1,-1) and right/top is (1,1) static constexpr float VERTEX_POSITION[] = { // Vertex ID Triangle on screen 0.0f, 1.0f, // 0 0 1.0f, 0.0f, // 1 . . -0.5f, 0.0f // 2 2.......1 }; mComputeInputVertexBuffer = mBufferManager->createVertexBuffer(sizeof(VERTEX_POSITION), VERTEX_POSITION, Renderer::BufferFlag::SHADER_RESOURCE); mComputeOutputVertexBuffer = mBufferManager->createVertexBuffer(sizeof(VERTEX_POSITION), nullptr, Renderer::BufferFlag::UNORDERED_ACCESS); } { // Create vertex array object (VAO) // -> The vertex array object (VAO) keeps a reference to the used vertex buffer object (VBO) // -> This means that there's no need to keep an own vertex buffer object (VBO) reference // -> When the vertex array object (VAO) is destroyed, it automatically decreases the // reference of the used vertex buffer objects (VBO). If the reference counter of a // vertex buffer object (VBO) reaches zero, it's automatically destroyed. const Renderer::VertexArrayVertexBuffer vertexArrayVertexBuffers[] = { mComputeOutputVertexBuffer }; mVertexArray = mBufferManager->createVertexArray(vertexAttributes, static_cast<uint32_t>(GLM_COUNTOF(vertexArrayVertexBuffers)), vertexArrayVertexBuffers, mComputeOutputIndexBuffer); } { // Create the uniform buffer which will be read by a compute shader static constexpr float RGBA_COLOR[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; mComputeInputUniformBuffer = mBufferManager->createUniformBuffer(sizeof(RGBA_COLOR), RGBA_COLOR); } { // Resource group related // Create the texture instance, but without providing texture data (we use the texture as render target) // -> Use the "Renderer::TextureFlag::RENDER_TARGET"-flag to mark this texture as a render target // -> Required for Vulkan, Direct3D 9, Direct3D 10, Direct3D 11 and Direct3D 12 // -> Not required for OpenGL and OpenGL ES 3 // -> The optimized texture clear value is a Direct3D 12 related option const Renderer::TextureFormat::Enum textureFormat = Renderer::TextureFormat::Enum::R8G8B8A8; Renderer::ITexture* computeInputTexture2D = mTextureManager->createTexture2D(16, 16, textureFormat, nullptr, Renderer::TextureFlag::SHADER_RESOURCE | Renderer::TextureFlag::RENDER_TARGET, Renderer::TextureUsage::DEFAULT, 1, reinterpret_cast<const Renderer::OptimizedTextureClearValue*>(&Color4::GREEN)); Renderer::ITexture* computeOutputTexture2D = mTextureManager->createTexture2D(16, 16, textureFormat, nullptr, Renderer::TextureFlag::SHADER_RESOURCE | Renderer::TextureFlag::UNORDERED_ACCESS); { // Create the framebuffer object (FBO) instance const Renderer::FramebufferAttachment colorFramebufferAttachment(computeInputTexture2D); mFramebuffer = renderer->createFramebuffer(*renderer->createRenderPass(1, &textureFormat), &colorFramebufferAttachment); } { // Create first compute resource group Renderer::IResource* resources[7] = { // Input computeInputTexture2D, mComputeInputIndexBuffer, mComputeInputVertexBuffer, mComputeInputUniformBuffer, // Output computeOutputTexture2D, mComputeOutputIndexBuffer, mComputeOutputVertexBuffer }; Renderer::ISamplerState* samplerStates[7] = { // Input static_cast<Renderer::ISamplerState*>(samplerStateResource), nullptr, nullptr, nullptr, // Output nullptr, nullptr, nullptr }; mComputeResourceGroup1 = mComputeRootSignature1->createResourceGroup(0, static_cast<uint32_t>(GLM_COUNTOF(resources)), resources, samplerStates); } { // Create second compute resource group Renderer::IResource* resources[6] = { // Input mComputeInputTextureBuffer, mComputeInputStructuredBuffer, mComputeInputIndirectBuffer, // Output mComputeOutputTextureBuffer, mComputeOutputStructuredBuffer, mComputeOutputIndirectBuffer }; mComputeResourceGroup2 = mComputeRootSignature2->createResourceGroup(0, static_cast<uint32_t>(GLM_COUNTOF(resources)), resources, nullptr); } { // Create graphics resource group Renderer::IResource* resources[4] = { mComputeInputUniformBuffer, mComputeOutputTextureBuffer, mComputeOutputStructuredBuffer, computeOutputTexture2D }; Renderer::ISamplerState* samplerStates[4] = { nullptr, nullptr, nullptr, static_cast<Renderer::ISamplerState*>(samplerStateResource) }; mGraphicsResourceGroup = mGraphicsRootSignature->createResourceGroup(0, static_cast<uint32_t>(GLM_COUNTOF(resources)), resources, samplerStates); } } { // Create the graphics program Renderer::IGraphicsProgramPtr graphicsProgram; { // Get the shader source code (outsourced to keep an overview) const char* vertexShaderSourceCode = nullptr; const char* fragmentShaderSourceCode = nullptr; const char* computeShaderSourceCode1 = nullptr; const char* computeShaderSourceCode2 = nullptr; #include "FirstComputeShader_GLSL_450.h" // For Vulkan #include "FirstComputeShader_GLSL_430.h" // macOS 10.11 only supports OpenGL 4.1 and hence can't be supported by this example #include "FirstComputeShader_HLSL_D3D11_D3D12.h" #include "FirstComputeShader_Null.h" // Create the graphics program Renderer::IShaderLanguage& shaderLanguage = renderer->getDefaultShaderLanguage(); graphicsProgram = shaderLanguage.createGraphicsProgram( *mGraphicsRootSignature, vertexAttributes, shaderLanguage.createVertexShaderFromSourceCode(vertexAttributes, vertexShaderSourceCode), shaderLanguage.createFragmentShaderFromSourceCode(fragmentShaderSourceCode)); // Create the compute pipeline state objects (PSO) mComputePipelineState1 = renderer->createComputePipelineState(*mComputeRootSignature1, *shaderLanguage.createComputeShaderFromSourceCode(computeShaderSourceCode1)); mComputePipelineState2 = renderer->createComputePipelineState(*mComputeRootSignature2, *shaderLanguage.createComputeShaderFromSourceCode(computeShaderSourceCode2)); } // Create the graphics pipeline state object (PSO) if (nullptr != graphicsProgram) { mGraphicsPipelineState = renderer->createGraphicsPipelineState(Renderer::GraphicsPipelineStateBuilder(mGraphicsRootSignature, graphicsProgram, vertexAttributes, getMainRenderTarget()->getRenderPass())); } } // Since we're always submitting the same commands to the renderer, we can fill the command buffer once during initialization and then reuse it multiple times during runtime fillCommandBuffer(); } }