/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #include #include #include #include #include #include #include #include #include #include #include namespace AtomSampleViewer { const char* TrianglesConstantBufferExampleComponent::s_trianglesConstantBufferExampleName = "TrianglesConstantBufferExample"; const uint32_t TrianglesConstantBufferExampleComponent::s_numberOfTrianglesTotal = 30; void TrianglesConstantBufferExampleComponent::Reflect(AZ::ReflectContext* context) { if (auto* serializeContext = azrtti_cast(context)) { serializeContext->Class() ->Version(0) ; } } TrianglesConstantBufferExampleComponent::TrianglesConstantBufferExampleComponent() { m_supportRHISamplePipeline = true; } void TrianglesConstantBufferExampleComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) { m_time += deltaTime; } void TrianglesConstantBufferExampleComponent::OnFramePrepare(AZ::RHI::FrameGraphBuilder& frameGraphBuilder) { using namespace AZ; InstanceInfo trianglesData[s_numberOfTrianglesTotal]; // Distribute the triangles evenly along the screen const uint32_t numColumns = static_cast(ceil(sqrt(s_numberOfTrianglesTotal))); const uint32_t lastTriangleIndex = s_numberOfTrianglesTotal - 1u; const uint32_t numRows = (lastTriangleIndex / numColumns) + 1u; // Last triangle's row index plus one is the actual number of rows for (uint32_t triangleIdx = 0u; triangleIdx < s_numberOfTrianglesTotal; ++triangleIdx) { // Location in range [0,1] const float offsetX = 1.0f / static_cast(numColumns); const float offsetY = 1.0f / static_cast(numRows); float x = (triangleIdx % numColumns) * offsetX + 0.5f * offsetX; float y = (triangleIdx / numColumns) * offsetY + 0.5f * offsetY; // Location in clip Space [0,1] -> [-1,1]. Triangles located top to bottom, left to right. x = 2.0f * x - 1.0f; y = -(2.0f * y - 1.0f); // Finally inverse y so it starts from top to bottom float time = m_time; time *= float(triangleIdx + 1u) * 0.2f; // Faster the bigger the index // Move the triangle around. AZ::Vector3 translation( x + sinf(time) * 0.05f, y + cosf(time) * 0.05f, 0.0f); //[GFX_TODO] ATOM-5582 - InstanceInfo should use Atom specific Matrix types AZ::Matrix4x4 translationMat = AZ::Matrix4x4::CreateTranslation(translation); translationMat.StoreToRowMajorFloat16(&trianglesData[triangleIdx].m_matrix[0]); AZ::Color colorMultiplier = fabsf(sinf(time)) * AZ::Color::CreateOne(); colorMultiplier.StoreToFloat4(&trianglesData[triangleIdx].m_colorMultiplier[0]); } // Calculate the alignment const uint32_t alignment = RHI::AlignUp(static_cast(sizeof(InstanceInfo)), m_constantBufferAlighment); // All triangles data will be uploaded in one go to the constant buffer once per frame. UploadDataToConstantBuffer(trianglesData, alignment, s_numberOfTrianglesTotal); BasicRHIComponent::OnFramePrepare(frameGraphBuilder); } void TrianglesConstantBufferExampleComponent::UploadDataToConstantBuffer(InstanceInfo* data, uint32_t elementSize, uint32_t elementCount) { AZ::RHI::BufferMapRequest mapRequest; mapRequest.m_buffer = m_constantBuffer.get(); mapRequest.m_byteCount = elementSize * elementCount; AZ::RHI::BufferMapResponse mapResponse; AZ::RHI::ResultCode resultCode = m_constantBufferPool->MapBuffer(mapRequest, mapResponse); if (resultCode == AZ::RHI::ResultCode::Success) { memcpy(mapResponse.m_data, data, sizeof(InstanceInfo) * elementCount); m_constantBufferPool->UnmapBuffer(*mapRequest.m_buffer); } } void TrianglesConstantBufferExampleComponent::Activate() { using namespace AZ; RHI::Ptr device = Utils::GetRHIDevice(); // Cache the alignment m_constantBufferAlighment = device->GetLimits().m_minConstantBufferViewOffset; AZ::RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor; // Creates Input Assembly buffer and Streams/Index Views { m_inputAssemblyBufferPool = RHI::Factory::Get().CreateBufferPool(); RHI::BufferPoolDescriptor bufferPoolDesc; bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly; bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device; m_inputAssemblyBufferPool->Init(*device, bufferPoolDesc); IABufferData bufferData; SetVertexPosition(bufferData.m_positions.data(), 0, 0.0f, 0.1f, 0.0); SetVertexPosition(bufferData.m_positions.data(), 1, -0.1f, -0.1f, 0.0); SetVertexPosition(bufferData.m_positions.data(), 2, 0.1f, -0.1f, 0.0); SetVertexColor(bufferData.m_colors.data(), 0, 1.0, 0.0, 0.0, 1.0); SetVertexColor(bufferData.m_colors.data(), 1, 0.0, 1.0, 0.0, 1.0); SetVertexColor(bufferData.m_colors.data(), 2, 0.0, 0.0, 1.0, 1.0); SetVertexIndexIncreasing(bufferData.m_indices.data(), bufferData.m_indices.size()); m_inputAssemblyBuffer = RHI::Factory::Get().CreateBuffer(); RHI::BufferInitRequest request; request.m_buffer = m_inputAssemblyBuffer.get(); request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::InputAssembly, sizeof(bufferData) }; request.m_initialData = &bufferData; m_inputAssemblyBufferPool->InitBuffer(request); m_streamBufferViews[0] = { *m_inputAssemblyBuffer, offsetof(IABufferData, m_positions), sizeof(IABufferData::m_positions), sizeof(VertexPosition) }; m_streamBufferViews[1] = { *m_inputAssemblyBuffer, offsetof(IABufferData, m_colors), sizeof(IABufferData::m_colors), sizeof(VertexColor) }; m_indexBufferView = { *m_inputAssemblyBuffer, offsetof(IABufferData, m_indices), sizeof(IABufferData::m_indices), RHI::IndexFormat::Uint16 }; RHI::InputStreamLayoutBuilder layoutBuilder; layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT); layoutBuilder.AddBuffer()->Channel("COLOR", RHI::Format::R32G32B32A32_FLOAT); pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End(); RHI::ValidateStreamBufferViews(pipelineStateDescriptor.m_inputStreamLayout, m_streamBufferViews); } // Create the buffer pool where both buffers get allocated from { m_constantBufferPool = RHI::Factory::Get().CreateBufferPool(); RHI::BufferPoolDescriptor constantBufferPoolDesc; constantBufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::Constant; constantBufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device; constantBufferPoolDesc.m_hostMemoryAccess = RHI::HostMemoryAccess::Write; [[maybe_unused]] RHI::ResultCode result = m_constantBufferPool->Init(*device, constantBufferPoolDesc); AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialized constant buffer pool"); } CreateConstantBufferView(); // Load the Shader and obtain the Pipeline state and its SRG { const char* triangeShaderFilePath = "Shaders/RHI/TrianglesConstantBuffer.azshader"; auto shader = LoadShader(triangeShaderFilePath, s_trianglesConstantBufferExampleName); if (shader == nullptr) return; auto shaderVariant = shader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId); shaderVariant.ConfigurePipelineState(pipelineStateDescriptor); RHI::RenderAttachmentLayoutBuilder attachmentsBuilder; attachmentsBuilder.AddSubpass() ->RenderTargetAttachment(m_outputFormat); [[maybe_unused]] RHI::ResultCode result = attachmentsBuilder.End(pipelineStateDescriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout); AZ_Assert(result == RHI::ResultCode::Success, "Failed to create render attachment layout"); m_pipelineState = shader->AcquirePipelineState(pipelineStateDescriptor); AZ_Assert(m_pipelineState, "Failed to acquire default pipeline state for shader '%s'", triangeShaderFilePath); m_shaderResourceGroup = CreateShaderResourceGroup(shader, "TriangleSrg", s_trianglesConstantBufferExampleName); } // Prepare SRG instance { // Find the constant buffer index inside the SRG const Name trianglesBufferId{ "m_trianglesCB" }; RHI::ShaderInputBufferIndex trianglesBufferIndex; FindShaderInputIndex(&trianglesBufferIndex, m_shaderResourceGroup, trianglesBufferId, s_trianglesConstantBufferExampleName); [[maybe_unused]] bool set = m_shaderResourceGroup->SetBufferView(trianglesBufferIndex, m_constantBufferView.get(), 0); AZ_Assert(set, "Failed to set the buffer view"); // All SRG data has been set already, it only had the buffer view to be set, we can compile now. // Later then only thing to do is to update the Buffer content itself, but the SRG won't need to be recompiled. m_shaderResourceGroup->Compile(); } // Creates a scope for rendering the triangle. { struct ScopeData { }; const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData) { // Binds the swap chain as a color attachment. Clears it to white. { RHI::ImageScopeAttachmentDescriptor descriptor; descriptor.m_attachmentId = m_outputAttachmentId; descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load; frameGraph.UseColorAttachment(descriptor); } // We will submit a single draw item. frameGraph.SetEstimatedItemCount(1); }; RHI::EmptyCompileFunction compileFunction; const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData) { RHI::CommandList* commandList = context.GetCommandList(); // Set persistent viewport and scissor state. commandList->SetViewports(&m_viewport, 1); commandList->SetScissors(&m_scissor, 1); RHI::DrawIndexed drawIndexed; drawIndexed.m_indexCount = 3; drawIndexed.m_instanceCount = s_numberOfTrianglesTotal; const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroup->GetRHIShaderResourceGroup() }; RHI::DrawItem drawItem; drawItem.m_arguments = drawIndexed; drawItem.m_pipelineState = m_pipelineState.get(); drawItem.m_indexBufferView = &m_indexBufferView; drawItem.m_shaderResourceGroupCount = static_cast(RHI::ArraySize(shaderResourceGroups)); drawItem.m_shaderResourceGroups = shaderResourceGroups; drawItem.m_streamBufferViewCount = static_cast(m_streamBufferViews.size()); drawItem.m_streamBufferViews = m_streamBufferViews.data(); // Submit the triangle draw item. commandList->Submit(drawItem); }; m_scopeProducers.emplace_back( aznew RHI::ScopeProducerFunction< ScopeData, decltype(prepareFunction), decltype(compileFunction), decltype(executeFunction)>( RHI::ScopeId{"TrianglesConstantBuffer"}, ScopeData{}, prepareFunction, compileFunction, executeFunction)); } TickBus::Handler::BusConnect(); RHI::RHISystemNotificationBus::Handler::BusConnect(); } void TrianglesConstantBufferExampleComponent::CreateConstantBufferView() { using namespace AZ; const uint32_t constantBufferSize = sizeof(InstanceInfo) * s_numberOfTrianglesTotal; m_constantBuffer = RHI::Factory::Get().CreateBuffer(); RHI::BufferInitRequest request; request.m_buffer = m_constantBuffer.get(); request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::Constant, constantBufferSize }; [[maybe_unused]] RHI::ResultCode result = m_constantBufferPool->InitBuffer(request); AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize constant buffer"); RHI::BufferViewDescriptor bufferDesc = RHI::BufferViewDescriptor::CreateStructured(0, 1u, constantBufferSize); m_constantBufferView = m_constantBuffer->GetBufferView(bufferDesc); } void TrianglesConstantBufferExampleComponent::Deactivate() { using namespace AZ; m_inputAssemblyBuffer = nullptr; m_inputAssemblyBufferPool = nullptr; m_constantBuffer = nullptr; m_constantBufferView = nullptr; m_constantBuffer = nullptr; m_constantBufferPool = nullptr; m_pipelineState = nullptr; m_shaderResourceGroup = nullptr; m_scopeProducers.clear(); m_windowContext = nullptr; TickBus::Handler::BusConnect(); RHI::RHISystemNotificationBus::Handler::BusDisconnect(); } } // namespace AtomSampleViewer