|
@@ -0,0 +1,871 @@
|
|
|
+/*
|
|
|
+ * 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 <RHI/VariableRateShadingExampleComponent.h>
|
|
|
+#include <Utils/Utils.h>
|
|
|
+
|
|
|
+#include <SampleComponentManager.h>
|
|
|
+
|
|
|
+#include <Atom/RHI/CommandList.h>
|
|
|
+#include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
|
|
|
+#include <Atom/RHI.Reflect/RenderAttachmentLayoutBuilder.h>
|
|
|
+#include <Atom/RHI.Reflect/VariableRateShadingEnums.h>
|
|
|
+#include <Atom/RPI.Public/Shader/Shader.h>
|
|
|
+#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
|
|
|
+#include <AzCore/Serialization/SerializeContext.h>
|
|
|
+#include <AzCore/Math/MathUtils.h>
|
|
|
+#include <AzCore/std/containers/span.h>
|
|
|
+#include <AzCore/Math/MatrixUtils.h>
|
|
|
+#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
|
|
|
+#include <AzFramework/Input/Devices/Touch/InputDeviceTouch.h>
|
|
|
+
|
|
|
+namespace AtomSampleViewer
|
|
|
+{
|
|
|
+ using namespace AZ;
|
|
|
+
|
|
|
+ namespace VariableRateShading
|
|
|
+ {
|
|
|
+ const char* SampleName = "VariableRateShadingExample";
|
|
|
+ const char* ShadingRateAttachmentId = "ShadingRateAttachmentId";
|
|
|
+ const char* ShadingRateAttachmentUpdateId = "ShadingRateAttachmentUpdateId";
|
|
|
+ }
|
|
|
+
|
|
|
+ RHI::Format ConvertToUInt(RHI::Format format)
|
|
|
+ {
|
|
|
+ uint32_t count = GetFormatComponentCount(format);
|
|
|
+ if (count == 1)
|
|
|
+ {
|
|
|
+ return RHI::Format::R8_UINT;
|
|
|
+ }
|
|
|
+ else if (count == 2)
|
|
|
+ {
|
|
|
+ return RHI::Format::R8G8_UINT;
|
|
|
+ }
|
|
|
+ return RHI::Format::R8G8B8A8_UINT;
|
|
|
+ }
|
|
|
+
|
|
|
+ const char* ToString(RHI::ShadingRate rate)
|
|
|
+ {
|
|
|
+ switch (rate)
|
|
|
+ {
|
|
|
+ case RHI::ShadingRate::Rate1x1: return "Rate1x1";
|
|
|
+ case RHI::ShadingRate::Rate1x2: return "Rate1x2";
|
|
|
+ case RHI::ShadingRate::Rate2x1: return "Rate2x1";
|
|
|
+ case RHI::ShadingRate::Rate2x2: return "Rate2x2";
|
|
|
+ case RHI::ShadingRate::Rate2x4: return "Rate2x4";
|
|
|
+ case RHI::ShadingRate::Rate4x2: return "Rate4x2";
|
|
|
+ case RHI::ShadingRate::Rate4x4: return "Rate4x4";
|
|
|
+ default: return "";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void VariableRateShadingExampleComponent::Reflect(AZ::ReflectContext* context)
|
|
|
+ {
|
|
|
+ if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
|
|
+ {
|
|
|
+ serializeContext->Class<VariableRateShadingExampleComponent, AZ::Component>()
|
|
|
+ ->Version(0)
|
|
|
+ ;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ VariableRateShadingExampleComponent::VariableRateShadingExampleComponent()
|
|
|
+ {
|
|
|
+ m_supportRHISamplePipeline = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ void VariableRateShadingExampleComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
|
|
+ {
|
|
|
+ if (m_imguiSidebar.Begin())
|
|
|
+ {
|
|
|
+ DrawSettings();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void VariableRateShadingExampleComponent::OnFramePrepare(AZ::RHI::FrameGraphBuilder& frameGraphBuilder)
|
|
|
+ {
|
|
|
+ if (m_windowContext->GetSwapChainsSize() && m_windowContext->GetSwapChain())
|
|
|
+ {
|
|
|
+ if (m_useImageShadingRate)
|
|
|
+ {
|
|
|
+ frameGraphBuilder.GetAttachmentDatabase().ImportImage(RHI::AttachmentId{ VariableRateShading::ShadingRateAttachmentId }, m_shadingRateImages[m_frameCount % m_shadingRateImages.size()]);
|
|
|
+ if (!Utils::GetRHIDevice()->GetFeatures().m_dynamicShadingRateImage)
|
|
|
+ {
|
|
|
+ // We cannot update and use the same shading rate image because "m_dynamicShadingRateImage" is not supported.
|
|
|
+ frameGraphBuilder.GetAttachmentDatabase().ImportImage(RHI::AttachmentId{ VariableRateShading::ShadingRateAttachmentUpdateId }, m_shadingRateImages[(m_frameCount + m_shadingRateImages.size() - 1) % m_shadingRateImages.size()]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ m_frameCount++;
|
|
|
+ }
|
|
|
+
|
|
|
+ BasicRHIComponent::OnFramePrepare(frameGraphBuilder);
|
|
|
+ }
|
|
|
+
|
|
|
+ void VariableRateShadingExampleComponent::Activate()
|
|
|
+ {
|
|
|
+ AZ::TickBus::Handler::BusConnect();
|
|
|
+ AZ::RHI::RHISystemNotificationBus::Handler::BusConnect();
|
|
|
+ AzFramework::InputChannelEventListener::Connect();
|
|
|
+
|
|
|
+
|
|
|
+ RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
|
|
|
+ const auto& deviceFeatures = device->GetFeatures();
|
|
|
+ if (!RHI::CheckBitsAll(deviceFeatures.m_shadingRateTypeMask, RHI::ShadingRateTypeFlags::PerImage))
|
|
|
+ {
|
|
|
+ m_useImageShadingRate = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!RHI::CheckBitsAll(deviceFeatures.m_shadingRateTypeMask, RHI::ShadingRateTypeFlags::PerDraw))
|
|
|
+ {
|
|
|
+ m_useDrawShadingRate = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (RHI::CheckBitsAll(deviceFeatures.m_shadingRateTypeMask, RHI::ShadingRateTypeFlags::PerImage))
|
|
|
+ {
|
|
|
+ for (uint32_t i = 0; i < static_cast<uint32_t>(RHI::Format::Count); ++i)
|
|
|
+ {
|
|
|
+ RHI::Format format = static_cast<RHI::Format>(i);
|
|
|
+ RHI::FormatCapabilities capabilities = device->GetFormatCapabilities(format);
|
|
|
+ if (RHI::CheckBitsAll(capabilities, RHI::FormatCapabilities::ShadingRate))
|
|
|
+ {
|
|
|
+ m_rateShadingImageFormat = format;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ AZ_Assert(m_rateShadingImageFormat != RHI::Format::Unknown, "Could not find a format for the shading rate image");
|
|
|
+ }
|
|
|
+
|
|
|
+ const auto& supportedMask = device->GetFeatures().m_shadingRateMask;
|
|
|
+ for (uint32_t i = 0; i < static_cast<uint32_t>(RHI::ShadingRate::Count); ++i)
|
|
|
+ {
|
|
|
+ if (RHI::CheckBitsAll(supportedMask, static_cast<RHI::ShadingRateFlags>(AZ_BIT(i))))
|
|
|
+ {
|
|
|
+ m_supportedModes.push_back(static_cast<RHI::ShadingRate>(i));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ m_shadingRate = m_supportedModes[0];
|
|
|
+
|
|
|
+ CreateShadingRateImage();
|
|
|
+ LoadShaders();
|
|
|
+ CreateInputAssemblyBuffersAndViews();
|
|
|
+ CreateShaderResourceGroups();
|
|
|
+ CreatePipelines();
|
|
|
+ CreateComputeScope();
|
|
|
+ CreateRenderScope();
|
|
|
+ CreatImageDisplayScope();
|
|
|
+ m_frameCount = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ void VariableRateShadingExampleComponent::CreateShadingRateImage()
|
|
|
+ {
|
|
|
+ RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
|
|
|
+
|
|
|
+ const auto& tileSize = device->GetLimits().m_shadingRateTileSize;
|
|
|
+ m_shadingRateImageSize = Vector2(ceil(static_cast<float>(m_outputWidth) / tileSize.m_width), ceil(static_cast<float>(m_outputHeight) / tileSize.m_height));
|
|
|
+
|
|
|
+ m_imagePool = RHI::Factory::Get().CreateImagePool();
|
|
|
+ RHI::ImagePoolDescriptor imagePoolDesc;
|
|
|
+ imagePoolDesc.m_bindFlags = RHI::ImageBindFlags::ShadingRate | RHI::ImageBindFlags::ShaderReadWrite;
|
|
|
+ m_imagePool->Init(*device, imagePoolDesc);
|
|
|
+
|
|
|
+ // Initialize the shading rate images with proper values. Invalid values may cause a crash.
|
|
|
+ uint32_t width = static_cast<uint32_t>(m_shadingRateImageSize.GetX());
|
|
|
+ uint32_t height = static_cast<uint32_t>(m_shadingRateImageSize.GetY());
|
|
|
+ uint32_t formatSize = GetFormatSize(m_rateShadingImageFormat);
|
|
|
+ uint32_t bufferSize = width * height * formatSize;
|
|
|
+ AZStd::vector<uint8_t> shadingRatePatternData(bufferSize);
|
|
|
+ // Use the lowest shading rate as the default value.
|
|
|
+ RHI::ShadingRateImageValue defaultValue = device->ConvertShadingRate(m_supportedModes[m_supportedModes.size() - 1]);
|
|
|
+ uint8_t* ptrData = shadingRatePatternData.data();
|
|
|
+ for (uint32_t y = 0; y < height; y++)
|
|
|
+ {
|
|
|
+ for (uint32_t x = 0; x < width; x++)
|
|
|
+ {
|
|
|
+ ::memcpy(ptrData, &defaultValue, formatSize);
|
|
|
+ ptrData += formatSize;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Since the device may not support "Dynamic Shading Rate Image", we need to buffer the update of the shading rate image
|
|
|
+ // because the CPU may be trying to read the image.
|
|
|
+ m_shadingRateImages.resize(device->GetFeatures().m_dynamicShadingRateImage ? 1 : device->GetDescriptor().m_frameCountMax+3);
|
|
|
+ for (auto& image : m_shadingRateImages)
|
|
|
+ {
|
|
|
+ image = RHI::Factory::Get().CreateImage();
|
|
|
+ RHI::ImageInitRequest initImageRequest;
|
|
|
+ RHI::ClearValue clearValue = RHI::ClearValue::CreateVector4Float(1, 1, 1, 1);
|
|
|
+ initImageRequest.m_image = image.get();
|
|
|
+ initImageRequest.m_descriptor = RHI::ImageDescriptor::Create2D(
|
|
|
+ imagePoolDesc.m_bindFlags,
|
|
|
+ static_cast<uint32_t>(m_shadingRateImageSize.GetX()),
|
|
|
+ static_cast<uint32_t>(m_shadingRateImageSize.GetY()),
|
|
|
+ m_rateShadingImageFormat);
|
|
|
+ initImageRequest.m_optimizedClearValue = &clearValue;
|
|
|
+ m_imagePool->InitImage(initImageRequest);
|
|
|
+
|
|
|
+ RHI::ImageUpdateRequest request;
|
|
|
+ request.m_image = image.get();
|
|
|
+ request.m_sourceData = shadingRatePatternData.data();
|
|
|
+ request.m_sourceSubresourceLayout = RHI::ImageSubresourceLayout(
|
|
|
+ RHI::Size(width, height, 1),
|
|
|
+ height,
|
|
|
+ width * formatSize,
|
|
|
+ bufferSize,
|
|
|
+ 1,
|
|
|
+ 1
|
|
|
+ );
|
|
|
+
|
|
|
+ m_imagePool->UpdateImageContents(request);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void VariableRateShadingExampleComponent::LoadShaders()
|
|
|
+ {
|
|
|
+ const char* shaders[] =
|
|
|
+ {
|
|
|
+ "Shaders/RHI/VariableRateShading.azshader",
|
|
|
+ "Shaders/RHI/VariableRateShadingCompute.azshader",
|
|
|
+ "Shaders/RHI/VariableRateShadingImage.azshader"
|
|
|
+ };
|
|
|
+
|
|
|
+ m_shaders.resize(AZ_ARRAY_SIZE(shaders));
|
|
|
+ for (size_t i = 0; i < AZ_ARRAY_SIZE(shaders); ++i)
|
|
|
+ {
|
|
|
+ auto shader = LoadShader(shaders[i], VariableRateShading::SampleName);
|
|
|
+ if (shader == nullptr)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ m_shaders[i] = shader;
|
|
|
+ }
|
|
|
+
|
|
|
+ const auto& numThreads = m_shaders[1]->GetAsset()->GetAttribute(RHI::ShaderStage::Compute, Name("numthreads"));
|
|
|
+ if (numThreads)
|
|
|
+ {
|
|
|
+ const RHI::ShaderStageAttributeArguments& args = *numThreads;
|
|
|
+ m_numThreadsX = args[0].type() == azrtti_typeid<int>() ? AZStd::any_cast<int>(args[0]) : m_numThreadsX;
|
|
|
+ m_numThreadsY = args[1].type() == azrtti_typeid<int>() ? AZStd::any_cast<int>(args[1]) : m_numThreadsY;
|
|
|
+ m_numThreadsZ = args[2].type() == azrtti_typeid<int>() ? AZStd::any_cast<int>(args[2]) : m_numThreadsZ;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void VariableRateShadingExampleComponent::CreateShaderResourceGroups()
|
|
|
+ {
|
|
|
+ const Name albedoId{ "m_texture" };
|
|
|
+ auto textureIamge = LoadStreamingImage("textures/bricks.png.streamingimage", VariableRateShading::SampleName);
|
|
|
+
|
|
|
+ AZ::RHI::ShaderInputImageIndex albedoIndex;
|
|
|
+ m_modelShaderResourceGroup = CreateShaderResourceGroup(m_shaders[0], "InstanceSrg", VariableRateShading::SampleName);
|
|
|
+ FindShaderInputIndex(&albedoIndex, m_modelShaderResourceGroup, albedoId, VariableRateShading::SampleName);
|
|
|
+ m_modelShaderResourceGroup->SetImage(albedoIndex, textureIamge);
|
|
|
+ m_modelShaderResourceGroup->Compile();
|
|
|
+
|
|
|
+ const Name centerId{ "m_center" };
|
|
|
+ const Name distancesId{ "m_distances" };
|
|
|
+ const Name patternId{ "m_pattern" };
|
|
|
+ const Name shadingRateImageId{ "m_shadingRateTexture" };
|
|
|
+
|
|
|
+ AZ::RHI::ShaderInputConstantIndex patternIndex;
|
|
|
+
|
|
|
+ m_computeShaderResourceGroup = CreateShaderResourceGroup(m_shaders[1], "ComputeSrg", VariableRateShading::SampleName);
|
|
|
+ FindShaderInputIndex(&patternIndex, m_computeShaderResourceGroup, patternId, VariableRateShading::SampleName);
|
|
|
+ FindShaderInputIndex(&m_centerIndex, m_computeShaderResourceGroup, centerId, VariableRateShading::SampleName);
|
|
|
+ FindShaderInputIndex(&m_shadingRateIndex, m_computeShaderResourceGroup, shadingRateImageId, VariableRateShading::SampleName);
|
|
|
+
|
|
|
+ RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
|
|
|
+
|
|
|
+ struct Pattern
|
|
|
+ {
|
|
|
+ float m_distance[4];
|
|
|
+ uint32_t m_rate[4];
|
|
|
+ };
|
|
|
+
|
|
|
+ struct Color
|
|
|
+ {
|
|
|
+ float m_color[4];
|
|
|
+ uint32_t m_rate[4];
|
|
|
+ };
|
|
|
+
|
|
|
+ const float alpha = 0.3f;
|
|
|
+ const uint32_t numRates = static_cast<uint32_t>(RHI::ShadingRate::Count);
|
|
|
+ AZStd::array<Pattern, numRates> pattern;
|
|
|
+ AZStd::array<Color, numRates> patternColors;
|
|
|
+ AZStd::array<AZ::Color, numRates> colors =
|
|
|
+ {{
|
|
|
+ AZ::Color(0.f, 0.f, 1.f, alpha),
|
|
|
+ AZ::Color(1.f, 0.f, 0.f, alpha),
|
|
|
+ AZ::Color(0.f, 1.f, 0.f, alpha),
|
|
|
+ AZ::Color(1.f, 0.f, 1.f, alpha),
|
|
|
+ AZ::Color(1.f, 1.f, 0.f, alpha),
|
|
|
+ AZ::Color(0.f, 1.f, 1.f, alpha),
|
|
|
+ AZ::Color(1.f, 1.f, 1.f, alpha)
|
|
|
+ }};
|
|
|
+
|
|
|
+ float range = 60.0f / numRates;
|
|
|
+ float currentRange = 8.0f;
|
|
|
+
|
|
|
+ const auto& supportedMask = device->GetFeatures().m_shadingRateMask;
|
|
|
+ for (uint32_t i = 0; i < pattern.size(); ++i)
|
|
|
+ {
|
|
|
+ RHI::ShadingRateImageValue rate = {};
|
|
|
+ pattern[i].m_distance[0] = 0.0f;
|
|
|
+ if (RHI::CheckBitsAll(supportedMask, static_cast<RHI::ShadingRateFlags>(AZ_BIT(i))))
|
|
|
+ {
|
|
|
+ rate = device->ConvertShadingRate(static_cast<RHI::ShadingRate>(i));
|
|
|
+ pattern[i].m_distance[0] = currentRange;
|
|
|
+ }
|
|
|
+ pattern[i].m_rate[0] = rate.m_x;
|
|
|
+ pattern[i].m_rate[1] = rate.m_y;
|
|
|
+ currentRange += range;
|
|
|
+
|
|
|
+ patternColors[i].m_rate[0] = pattern[i].m_rate[0];
|
|
|
+ patternColors[i].m_rate[1] = pattern[i].m_rate[1];
|
|
|
+ colors[i].StoreToFloat4(patternColors[i].m_color);
|
|
|
+ }
|
|
|
+
|
|
|
+ Vector2 center(static_cast<float>(m_shadingRateImageSize.GetX()) * 0.5f, static_cast<float>(m_shadingRateImageSize.GetY()) * 0.5f);
|
|
|
+ m_computeShaderResourceGroup->SetConstant(m_centerIndex, center);
|
|
|
+ m_computeShaderResourceGroup->SetConstantArray(patternIndex, pattern);
|
|
|
+
|
|
|
+ const Name colorsId{ "m_colors" };
|
|
|
+ const Name textureId{ "m_texture" };
|
|
|
+ AZ::RHI::ShaderInputConstantIndex colorsIndex;
|
|
|
+
|
|
|
+ m_imageShaderResourceGroup = CreateShaderResourceGroup(m_shaders[2], "InstanceSrg", VariableRateShading::SampleName);
|
|
|
+ FindShaderInputIndex(&colorsIndex, m_imageShaderResourceGroup, colorsId, VariableRateShading::SampleName);
|
|
|
+ FindShaderInputIndex(&m_shadingRateDisplayIndex, m_imageShaderResourceGroup, textureId, VariableRateShading::SampleName);
|
|
|
+ m_imageShaderResourceGroup->SetConstantArray(colorsIndex, patternColors);
|
|
|
+ }
|
|
|
+
|
|
|
+ void VariableRateShadingExampleComponent::CreatePipelines()
|
|
|
+ {
|
|
|
+ {
|
|
|
+ // We create one pipeline when using a shading rate attachment, and another one when we are not using it.
|
|
|
+ RHI::RenderAttachmentLayoutBuilder shadingRateAttachmentsBuilder;
|
|
|
+ shadingRateAttachmentsBuilder.AddSubpass()
|
|
|
+ ->RenderTargetAttachment(m_outputFormat)
|
|
|
+ ->ShadingRateAttachment(m_rateShadingImageFormat);
|
|
|
+
|
|
|
+ RHI::RenderAttachmentLayout shadingRateRenderAttachmentLayout;
|
|
|
+ [[maybe_unused]] RHI::ResultCode result = shadingRateAttachmentsBuilder.End(shadingRateRenderAttachmentLayout);
|
|
|
+ AZ_Assert(result == RHI::ResultCode::Success, "Failed to create render attachment layout");
|
|
|
+
|
|
|
+ const auto& shader = m_shaders[0];
|
|
|
+ auto& variant = shader->GetVariant(AZ::RPI::ShaderAsset::RootShaderVariantStableId);
|
|
|
+
|
|
|
+ RHI::PipelineStateDescriptorForDraw pipelineDesc;
|
|
|
+ variant.ConfigurePipelineState(pipelineDesc);
|
|
|
+ pipelineDesc.m_renderStates.m_depthStencilState = RHI::DepthStencilState::CreateDisabled();
|
|
|
+ pipelineDesc.m_renderAttachmentConfiguration.m_renderAttachmentLayout = shadingRateRenderAttachmentLayout;
|
|
|
+ pipelineDesc.m_renderAttachmentConfiguration.m_subpassIndex = 0;
|
|
|
+ pipelineDesc.m_inputStreamLayout = m_inputStreamLayout;
|
|
|
+
|
|
|
+ m_modelPipelineState[0] = shader->AcquirePipelineState(pipelineDesc);
|
|
|
+ if (!m_modelPipelineState[0])
|
|
|
+ {
|
|
|
+ AZ_Error(VariableRateShading::SampleName, false, "Failed to acquire default pipeline state for shader");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ RHI::RenderAttachmentLayoutBuilder attachmentsBuilder;
|
|
|
+ attachmentsBuilder.AddSubpass()
|
|
|
+ ->RenderTargetAttachment(m_outputFormat);
|
|
|
+
|
|
|
+ RHI::RenderAttachmentLayout rateRenderAttachmentLayout;
|
|
|
+ result = attachmentsBuilder.End(rateRenderAttachmentLayout);
|
|
|
+ AZ_Assert(result == RHI::ResultCode::Success, "Failed to create render attachment layout");
|
|
|
+ pipelineDesc.m_renderAttachmentConfiguration.m_renderAttachmentLayout = rateRenderAttachmentLayout;
|
|
|
+
|
|
|
+ m_modelPipelineState[1] = shader->AcquirePipelineState(pipelineDesc);
|
|
|
+ if (!m_modelPipelineState[1])
|
|
|
+ {
|
|
|
+ AZ_Error(VariableRateShading::SampleName, false, "Failed to acquire default pipeline state for shader");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ RHI::PipelineStateDescriptorForDispatch pipelineDesc;
|
|
|
+ const auto& shader = m_shaders[1];
|
|
|
+ shader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId).ConfigurePipelineState(pipelineDesc);
|
|
|
+
|
|
|
+ m_computePipelineState = shader->AcquirePipelineState(pipelineDesc);
|
|
|
+ if (!m_computePipelineState)
|
|
|
+ {
|
|
|
+ AZ_Error(VariableRateShading::SampleName, false, "Failed to acquire default pipeline state for compute");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ RHI::RenderAttachmentLayoutBuilder attachmentsBuilder;
|
|
|
+ attachmentsBuilder.AddSubpass()
|
|
|
+ ->RenderTargetAttachment(m_outputFormat);
|
|
|
+
|
|
|
+ RHI::RenderAttachmentLayout renderAttachmentLayout;
|
|
|
+ [[maybe_unused]] RHI::ResultCode result = attachmentsBuilder.End(renderAttachmentLayout);
|
|
|
+ AZ_Assert(result == RHI::ResultCode::Success, "Failed to create render attachment layout");
|
|
|
+
|
|
|
+ const auto& shader = m_shaders[2];
|
|
|
+ auto& variant = shader->GetVariant(AZ::RPI::ShaderAsset::RootShaderVariantStableId);
|
|
|
+
|
|
|
+ RHI::PipelineStateDescriptorForDraw pipelineDesc;
|
|
|
+ variant.ConfigurePipelineState(pipelineDesc);
|
|
|
+ pipelineDesc.m_renderStates.m_depthStencilState = RHI::DepthStencilState::CreateDisabled();
|
|
|
+ pipelineDesc.m_renderAttachmentConfiguration.m_renderAttachmentLayout = renderAttachmentLayout;
|
|
|
+ pipelineDesc.m_renderAttachmentConfiguration.m_subpassIndex = 0;
|
|
|
+ pipelineDesc.m_inputStreamLayout = m_inputStreamLayout;
|
|
|
+
|
|
|
+ RHI::TargetBlendState& targetBlendState = pipelineDesc.m_renderStates.m_blendState.m_targets[0];
|
|
|
+ targetBlendState.m_enable = true;
|
|
|
+ targetBlendState.m_blendSource = RHI::BlendFactor::AlphaSource;
|
|
|
+ targetBlendState.m_blendDest = RHI::BlendFactor::AlphaSourceInverse;
|
|
|
+ targetBlendState.m_blendOp = RHI::BlendOp::Add;
|
|
|
+
|
|
|
+ m_imagePipelineState = shader->AcquirePipelineState(pipelineDesc);
|
|
|
+ if (!m_imagePipelineState)
|
|
|
+ {
|
|
|
+ AZ_Error(VariableRateShading::SampleName, false, "Failed to acquire default pipeline state for shader");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void VariableRateShadingExampleComponent::CreateInputAssemblyBuffersAndViews()
|
|
|
+ {
|
|
|
+ const RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
|
|
|
+
|
|
|
+ m_bufferPool = RHI::Factory::Get().CreateBufferPool();
|
|
|
+ RHI::BufferPoolDescriptor bufferPoolDesc;
|
|
|
+ bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
|
|
|
+ bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
|
|
|
+ m_bufferPool->Init(*device, bufferPoolDesc);
|
|
|
+
|
|
|
+ struct BufferData
|
|
|
+ {
|
|
|
+ AZStd::array<VertexPosition, 4> m_positions;
|
|
|
+ AZStd::array<VertexUV, 4> m_uvs;
|
|
|
+ AZStd::array<uint16_t, 6> m_indices;
|
|
|
+ };
|
|
|
+
|
|
|
+ BufferData bufferData;
|
|
|
+ SetFullScreenRect(bufferData.m_positions.data(), bufferData.m_uvs.data(), bufferData.m_indices.data());
|
|
|
+
|
|
|
+ m_inputAssemblyBuffer = RHI::Factory::Get().CreateBuffer();
|
|
|
+ RHI::ResultCode result = RHI::ResultCode::Success;
|
|
|
+ RHI::BufferInitRequest request;
|
|
|
+
|
|
|
+ request.m_buffer = m_inputAssemblyBuffer.get();
|
|
|
+ request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::InputAssembly, sizeof(bufferData) };
|
|
|
+ request.m_initialData = &bufferData;
|
|
|
+ result = m_bufferPool->InitBuffer(request);
|
|
|
+ if (result != RHI::ResultCode::Success)
|
|
|
+ {
|
|
|
+ AZ_Error(VariableRateShading::SampleName, false, "Failed to initialize buffer with error code %d", result);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ m_streamBufferViews[0] =
|
|
|
+ {
|
|
|
+ *m_inputAssemblyBuffer,
|
|
|
+ offsetof(BufferData, m_positions),
|
|
|
+ sizeof(BufferData::m_positions),
|
|
|
+ sizeof(VertexPosition)
|
|
|
+ };
|
|
|
+
|
|
|
+ m_streamBufferViews[1] =
|
|
|
+ {
|
|
|
+ *m_inputAssemblyBuffer,
|
|
|
+ offsetof(BufferData, m_uvs),
|
|
|
+ sizeof(BufferData::m_uvs),
|
|
|
+ sizeof(VertexUV)
|
|
|
+ };
|
|
|
+
|
|
|
+ m_indexBufferView =
|
|
|
+ {
|
|
|
+ *m_inputAssemblyBuffer,
|
|
|
+ offsetof(BufferData, m_indices),
|
|
|
+ sizeof(BufferData::m_indices),
|
|
|
+ RHI::IndexFormat::Uint16
|
|
|
+ };
|
|
|
+
|
|
|
+ RHI::InputStreamLayoutBuilder layoutBuilder;
|
|
|
+ layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
|
|
|
+ layoutBuilder.AddBuffer()->Channel("UV", RHI::Format::R32G32_FLOAT);
|
|
|
+ m_inputStreamLayout = layoutBuilder.End();
|
|
|
+
|
|
|
+ RHI::ValidateStreamBufferViews(m_inputStreamLayout, m_streamBufferViews);
|
|
|
+ }
|
|
|
+
|
|
|
+ void VariableRateShadingExampleComponent::CreateRenderScope()
|
|
|
+ {
|
|
|
+ struct ScopeData
|
|
|
+ {
|
|
|
+ };
|
|
|
+
|
|
|
+ const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
|
|
|
+ {
|
|
|
+ {
|
|
|
+ // Binds the swap chain as a color attachment.
|
|
|
+ RHI::ImageScopeAttachmentDescriptor descriptor;
|
|
|
+ descriptor.m_attachmentId = m_outputAttachmentId;
|
|
|
+ descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
|
|
|
+ frameGraph.UseColorAttachment(descriptor);
|
|
|
+ }
|
|
|
+
|
|
|
+ RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
|
|
|
+ bool useImageShadingRate = m_useImageShadingRate && (device->GetFeatures().m_dynamicShadingRateImage || m_frameCount > device->GetDescriptor().m_frameCountMax);
|
|
|
+ if (useImageShadingRate)
|
|
|
+ {
|
|
|
+ // Binds the shading rate image attachment
|
|
|
+ AZ::RHI::ImageScopeAttachmentDescriptor dsDesc;
|
|
|
+ dsDesc.m_attachmentId = VariableRateShading::ShadingRateAttachmentId;
|
|
|
+ dsDesc.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Load;
|
|
|
+ dsDesc.m_loadStoreAction.m_storeAction = AZ::RHI::AttachmentStoreAction::DontCare;
|
|
|
+ frameGraph.UseAttachment(dsDesc, AZ::RHI::ScopeAttachmentAccess::Read, AZ::RHI::ScopeAttachmentUsage::ShadingRate);
|
|
|
+ }
|
|
|
+
|
|
|
+ frameGraph.SetEstimatedItemCount(1);
|
|
|
+ };
|
|
|
+
|
|
|
+ RHI::EmptyCompileFunction<ScopeData> compileFunction;
|
|
|
+
|
|
|
+ const auto executeFunction = [this]([[maybe_unused]] 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::Ptr<RHI::Device> device = Utils::GetRHIDevice();
|
|
|
+ if (m_useDrawShadingRate)
|
|
|
+ {
|
|
|
+ RHI::ShadingRateCombinators combinators = { RHI::ShadingRateCombinerOp::Passthrough, m_combinerOp };
|
|
|
+ commandList->SetFragmentShadingRate(m_shadingRate, combinators);
|
|
|
+ }
|
|
|
+
|
|
|
+ const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_modelShaderResourceGroup->GetRHIShaderResourceGroup() };
|
|
|
+ // We have to wait until the updating of the initial contents of the shading rate image is done if
|
|
|
+ // dynamic mode is not supported (since the CPU would try to read it while the GPU is updating the contents)
|
|
|
+ bool useImageShadingRate = m_useImageShadingRate && (device->GetFeatures().m_dynamicShadingRateImage || m_frameCount > device->GetDescriptor().m_frameCountMax);
|
|
|
+
|
|
|
+ RHI::DrawIndexed drawIndexed;
|
|
|
+ drawIndexed.m_indexCount = 6;
|
|
|
+ drawIndexed.m_instanceCount = 1;
|
|
|
+
|
|
|
+ RHI::DrawItem drawItem;
|
|
|
+ drawItem.m_arguments = drawIndexed;
|
|
|
+ drawItem.m_pipelineState = m_modelPipelineState[useImageShadingRate ? 0 : 1].get();
|
|
|
+ drawItem.m_indexBufferView = &m_indexBufferView;
|
|
|
+ drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));;
|
|
|
+ drawItem.m_shaderResourceGroups = shaderResourceGroups;
|
|
|
+ drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
|
|
|
+ drawItem.m_streamBufferViews = m_streamBufferViews.data();
|
|
|
+
|
|
|
+ commandList->Submit(drawItem);
|
|
|
+ };
|
|
|
+
|
|
|
+ const RHI::ScopeId forwardScope("SceneScope");
|
|
|
+ m_scopeProducers.emplace_back(
|
|
|
+ aznew RHI::ScopeProducerFunction<
|
|
|
+ ScopeData,
|
|
|
+ decltype(prepareFunction),
|
|
|
+ decltype(compileFunction),
|
|
|
+ decltype(executeFunction)>(
|
|
|
+ forwardScope,
|
|
|
+ ScopeData{},
|
|
|
+ prepareFunction,
|
|
|
+ compileFunction,
|
|
|
+ executeFunction));
|
|
|
+ }
|
|
|
+
|
|
|
+ void VariableRateShadingExampleComponent::CreateComputeScope()
|
|
|
+ {
|
|
|
+ struct ScopeData
|
|
|
+ {
|
|
|
+ };
|
|
|
+
|
|
|
+ RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
|
|
|
+ const auto& deviceFeatures = device->GetFeatures();
|
|
|
+ // If "m_dynamicShadingRateImage" is not supported we cannot update the same image that is being used as shading rate this frame.
|
|
|
+ // We use an "old" one that is not longer in used.
|
|
|
+ const char* shadingRateAttachmentId = deviceFeatures.m_dynamicShadingRateImage ? VariableRateShading::ShadingRateAttachmentId : VariableRateShading::ShadingRateAttachmentUpdateId;
|
|
|
+ const auto prepareFunction = [this, shadingRateAttachmentId](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
|
|
|
+ {
|
|
|
+ if (m_useImageShadingRate)
|
|
|
+ {
|
|
|
+ RHI::ImageScopeAttachmentDescriptor shadingRateImageDesc;
|
|
|
+ shadingRateImageDesc.m_attachmentId = shadingRateAttachmentId;
|
|
|
+ shadingRateImageDesc.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::DontCare;
|
|
|
+ shadingRateImageDesc.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::Store;
|
|
|
+ shadingRateImageDesc.m_imageViewDescriptor.m_overrideFormat = ConvertToUInt(m_rateShadingImageFormat);
|
|
|
+ frameGraph.UseShaderAttachment(shadingRateImageDesc, RHI::ScopeAttachmentAccess::Write);
|
|
|
+ }
|
|
|
+
|
|
|
+ frameGraph.SetEstimatedItemCount(1);
|
|
|
+ };
|
|
|
+
|
|
|
+ const auto compileFunction = [this, shadingRateAttachmentId](const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
|
|
|
+ {
|
|
|
+ if (m_useImageShadingRate)
|
|
|
+ {
|
|
|
+ Vector2 center = m_cursorPos * m_shadingRateImageSize;
|
|
|
+ const RHI::ImageView* shadingRateImageView = context.GetImageView(RHI::AttachmentId(shadingRateAttachmentId));
|
|
|
+ m_computeShaderResourceGroup->SetImageView(m_shadingRateIndex, shadingRateImageView);
|
|
|
+ m_computeShaderResourceGroup->SetConstant(m_centerIndex, center);
|
|
|
+ m_computeShaderResourceGroup->Compile();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
|
|
|
+ {
|
|
|
+ if (!m_useImageShadingRate)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ RHI::CommandList* commandList = context.GetCommandList();
|
|
|
+
|
|
|
+ RHI::DispatchItem dispatchItem;
|
|
|
+ decltype(dispatchItem.m_shaderResourceGroups) shaderResourceGroups = { { m_computeShaderResourceGroup->GetRHIShaderResourceGroup() } };
|
|
|
+
|
|
|
+ RHI::DispatchDirect dispatchArgs;
|
|
|
+
|
|
|
+ dispatchArgs.m_totalNumberOfThreadsX = aznumeric_cast<uint32_t>(m_shadingRateImageSize.GetX());
|
|
|
+ dispatchArgs.m_threadsPerGroupX = aznumeric_cast<uint16_t>(m_numThreadsX);
|
|
|
+ dispatchArgs.m_totalNumberOfThreadsY = aznumeric_cast<uint32_t>(m_shadingRateImageSize.GetY());
|
|
|
+ dispatchArgs.m_threadsPerGroupY = aznumeric_cast<uint16_t>(m_numThreadsY);
|
|
|
+ dispatchArgs.m_totalNumberOfThreadsZ = 1;
|
|
|
+ dispatchArgs.m_threadsPerGroupZ = aznumeric_cast<uint16_t>(m_numThreadsZ);
|
|
|
+
|
|
|
+ AZ_Assert(dispatchArgs.m_threadsPerGroupX == dispatchArgs.m_threadsPerGroupY, "If the shader source changes, this logic should change too.");
|
|
|
+ AZ_Assert(dispatchArgs.m_threadsPerGroupZ == 1, "If the shader source changes, this logic should change too.");
|
|
|
+
|
|
|
+ dispatchItem.m_arguments = dispatchArgs;
|
|
|
+ dispatchItem.m_pipelineState = m_computePipelineState.get();
|
|
|
+ dispatchItem.m_shaderResourceGroupCount = 1;
|
|
|
+ dispatchItem.m_shaderResourceGroups = shaderResourceGroups;
|
|
|
+
|
|
|
+ commandList->Submit(dispatchItem);
|
|
|
+ };
|
|
|
+
|
|
|
+ const RHI::ScopeId computeScope("ShadingRateImageCompute");
|
|
|
+ m_scopeProducers.emplace_back(
|
|
|
+ aznew RHI::ScopeProducerFunction<
|
|
|
+ ScopeData,
|
|
|
+ decltype(prepareFunction),
|
|
|
+ decltype(compileFunction),
|
|
|
+ decltype(executeFunction)>(
|
|
|
+ computeScope,
|
|
|
+ ScopeData{},
|
|
|
+ prepareFunction,
|
|
|
+ compileFunction,
|
|
|
+ executeFunction));
|
|
|
+ }
|
|
|
+
|
|
|
+ void VariableRateShadingExampleComponent::CreatImageDisplayScope()
|
|
|
+ {
|
|
|
+ struct ScopeData
|
|
|
+ {
|
|
|
+ };
|
|
|
+
|
|
|
+ const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
|
|
|
+ {
|
|
|
+ {
|
|
|
+ // Binds the swap chain as a color attachment.
|
|
|
+ RHI::ImageScopeAttachmentDescriptor descriptor;
|
|
|
+ descriptor.m_attachmentId = m_outputAttachmentId;
|
|
|
+ descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
|
|
|
+ frameGraph.UseColorAttachment(descriptor);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (m_showShadingRateImage)
|
|
|
+ {
|
|
|
+ // Binds the shading rate image for reading (not as attachment)
|
|
|
+ RHI::ImageScopeAttachmentDescriptor shadingRateImageDesc;
|
|
|
+ shadingRateImageDesc.m_attachmentId = VariableRateShading::ShadingRateAttachmentId;
|
|
|
+ shadingRateImageDesc.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::DontCare;
|
|
|
+ shadingRateImageDesc.m_imageViewDescriptor.m_overrideFormat = ConvertToUInt(m_rateShadingImageFormat);
|
|
|
+ frameGraph.UseShaderAttachment(shadingRateImageDesc, RHI::ScopeAttachmentAccess::Read);
|
|
|
+ }
|
|
|
+
|
|
|
+ frameGraph.SetEstimatedItemCount(1);
|
|
|
+ };
|
|
|
+
|
|
|
+ const auto compileFunction = [this](const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
|
|
|
+ {
|
|
|
+ if (m_showShadingRateImage)
|
|
|
+ {
|
|
|
+ const RHI::ImageView* shadingRateImageView = context.GetImageView(RHI::AttachmentId(VariableRateShading::ShadingRateAttachmentId));
|
|
|
+ m_imageShaderResourceGroup->SetImageView(m_shadingRateDisplayIndex, shadingRateImageView);
|
|
|
+ m_imageShaderResourceGroup->Compile();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
|
|
|
+ {
|
|
|
+ if (!m_showShadingRateImage)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ RHI::CommandList* commandList = context.GetCommandList();
|
|
|
+
|
|
|
+ // Set persistent viewport and scissor state.
|
|
|
+ commandList->SetViewports(&m_viewport, 1);
|
|
|
+ commandList->SetScissors(&m_scissor, 1);
|
|
|
+
|
|
|
+ const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_imageShaderResourceGroup->GetRHIShaderResourceGroup() };
|
|
|
+
|
|
|
+ RHI::DrawIndexed drawIndexed;
|
|
|
+ drawIndexed.m_indexCount = 6;
|
|
|
+ drawIndexed.m_instanceCount = 1;
|
|
|
+
|
|
|
+ RHI::DrawItem drawItem;
|
|
|
+ drawItem.m_arguments = drawIndexed;
|
|
|
+ drawItem.m_pipelineState = m_imagePipelineState.get();
|
|
|
+ drawItem.m_indexBufferView = &m_indexBufferView;
|
|
|
+ drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
|
|
|
+ drawItem.m_shaderResourceGroups = shaderResourceGroups;
|
|
|
+ drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
|
|
|
+ drawItem.m_streamBufferViews = m_streamBufferViews.data();
|
|
|
+
|
|
|
+ commandList->Submit(drawItem);
|
|
|
+ };
|
|
|
+
|
|
|
+ const RHI::ScopeId forwardScope("ImageDisplayScope");
|
|
|
+ m_scopeProducers.emplace_back(
|
|
|
+ aznew RHI::ScopeProducerFunction<
|
|
|
+ ScopeData,
|
|
|
+ decltype(prepareFunction),
|
|
|
+ decltype(compileFunction),
|
|
|
+ decltype(executeFunction)>(
|
|
|
+ forwardScope,
|
|
|
+ ScopeData{},
|
|
|
+ prepareFunction,
|
|
|
+ compileFunction,
|
|
|
+ executeFunction));
|
|
|
+ }
|
|
|
+
|
|
|
+ void VariableRateShadingExampleComponent::Deactivate()
|
|
|
+ {
|
|
|
+ m_imguiSidebar.Deactivate();
|
|
|
+ AZ::RHI::RHISystemNotificationBus::Handler::BusDisconnect();
|
|
|
+ AZ::TickBus::Handler::BusDisconnect();
|
|
|
+ AzFramework::InputChannelEventListener::BusDisconnect();
|
|
|
+
|
|
|
+ m_bufferPool = nullptr;
|
|
|
+ m_inputAssemblyBuffer = nullptr;
|
|
|
+ m_modelPipelineState[0] = nullptr;
|
|
|
+ m_modelPipelineState[1] = nullptr;
|
|
|
+ m_imagePipelineState = nullptr;
|
|
|
+ m_modelShaderResourceGroup = nullptr;
|
|
|
+ m_computeShaderResourceGroup = nullptr;
|
|
|
+ m_imageShaderResourceGroup = nullptr;
|
|
|
+ m_shaders.clear();
|
|
|
+ m_supportedModes.clear();
|
|
|
+ m_windowContext = nullptr;
|
|
|
+ m_imagePool = nullptr;
|
|
|
+ m_shadingRateImages.clear();
|
|
|
+ m_scopeProducers.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ void VariableRateShadingExampleComponent::DrawSettings()
|
|
|
+ {
|
|
|
+ RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
|
|
|
+ const auto& deviceFeatures = device->GetFeatures();
|
|
|
+
|
|
|
+ ImGui::Spacing();
|
|
|
+ if (RHI::CheckBitsAll(deviceFeatures.m_shadingRateTypeMask, RHI::ShadingRateTypeFlags::PerImage))
|
|
|
+ {
|
|
|
+ ScriptableImGui::Checkbox("Image Shade Rate", &m_useImageShadingRate);
|
|
|
+ if (m_useImageShadingRate)
|
|
|
+ {
|
|
|
+ ImGui::Indent();
|
|
|
+ ScriptableImGui::Checkbox("Show Image", &m_showShadingRateImage);
|
|
|
+ ScriptableImGui::Checkbox("Follow Pointer", &m_followPointer);
|
|
|
+ ImGui::Unindent();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ m_showShadingRateImage = false;
|
|
|
+ m_followPointer = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (RHI::CheckBitsAll(deviceFeatures.m_shadingRateTypeMask, RHI::ShadingRateTypeFlags::PerDraw))
|
|
|
+ {
|
|
|
+ ScriptableImGui::Checkbox("Draw Shade Rate", &m_useDrawShadingRate);
|
|
|
+ if (m_useDrawShadingRate)
|
|
|
+ {
|
|
|
+ ImGui::Indent();
|
|
|
+ AZStd::vector<const char*> items;
|
|
|
+ for(const auto rate : m_supportedModes)
|
|
|
+ {
|
|
|
+ items.push_back(ToString(rate));
|
|
|
+ }
|
|
|
+ int current_item = static_cast<int>(AZStd::distance(m_supportedModes.begin(), AZStd::find(m_supportedModes.begin(), m_supportedModes.end(), m_shadingRate)));
|
|
|
+ ScriptableImGui::Combo("Shading Rates", ¤t_item, items.data(), static_cast<int>(items.size()));
|
|
|
+ m_shadingRate = m_supportedModes[current_item];
|
|
|
+ ImGui::Unindent();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (m_useDrawShadingRate && m_useImageShadingRate)
|
|
|
+ {
|
|
|
+ AZStd::vector<const char*> items = { "Passthrough", "Override", "Min", "Max" };
|
|
|
+ int current_item = static_cast<int>(m_combinerOp);
|
|
|
+ ScriptableImGui::Combo("Combiner Op", ¤t_item, items.data(), static_cast<int>(items.size()));
|
|
|
+ m_combinerOp = static_cast<RHI::ShadingRateCombinerOp>(current_item);
|
|
|
+ }
|
|
|
+ else if(m_useDrawShadingRate)
|
|
|
+ {
|
|
|
+ m_combinerOp = RHI::ShadingRateCombinerOp::Passthrough;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!m_followPointer)
|
|
|
+ {
|
|
|
+ m_cursorPos = AZ::Vector2(0.5f, 0.5f);
|
|
|
+ }
|
|
|
+
|
|
|
+ m_imguiSidebar.End();
|
|
|
+ }
|
|
|
+
|
|
|
+ bool VariableRateShadingExampleComponent::OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel)
|
|
|
+ {
|
|
|
+ if (m_followPointer)
|
|
|
+ {
|
|
|
+ const AzFramework::InputChannelId& inputChannelId = inputChannel.GetInputChannelId();
|
|
|
+ switch (inputChannel.GetState())
|
|
|
+ {
|
|
|
+ case AzFramework::InputChannel::State::Began:
|
|
|
+ case AzFramework::InputChannel::State::Updated: // update the camera rotation
|
|
|
+ {
|
|
|
+ const AzFramework::InputChannel::PositionData2D* position = nullptr;
|
|
|
+ // Mouse or Touch Events
|
|
|
+ if (inputChannelId == AzFramework::InputDeviceMouse::SystemCursorPosition ||
|
|
|
+ inputChannelId == AzFramework::InputDeviceTouch::Touch::Index0)
|
|
|
+ {
|
|
|
+ position = inputChannel.GetCustomData<AzFramework::InputChannel::PositionData2D>();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (position)
|
|
|
+ {
|
|
|
+ m_cursorPos = position->m_normalizedPosition;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+} // namespace AtomSampleViewer
|