From ecdc2872475b9b2952753221bc8327e706a98a02 Mon Sep 17 00:00:00 2001 From: lukasino1214 Date: Wed, 12 Nov 2025 12:45:14 +0100 Subject: [PATCH 1/4] adding rt visual test and fixing a lot of stuff --- ShockGraph/Resources.cpp | 14 +- ShockGraph/Resources.hpp | 45 ++++ ShockGraph/Task.cpp | 6 + ShockGraph/Task.hpp | 18 ++ ShockGraph/TaskCommandList.hpp | 60 ++++- ShockGraph/TaskGraph.cpp | 33 ++- ShockGraph/TaskResourceManager.cpp | 54 ++++- ShockGraph/TaskResourceManager.hpp | 2 + VisualTests/App.cpp | 10 +- VisualTests/Tests/AlphaToCoverage.cpp | 2 +- VisualTests/Tests/BlitImage.cpp | 34 ++- VisualTests/Tests/MSAA.cpp | 30 +-- VisualTests/Tests/RayTracingCompute.cpp | 223 +++++++++++++++++++ VisualTests/Tests/RayTracingCompute.hpp | 56 +++++ VisualTests/main.cpp | 2 + resources/VisualTests/Shaders/RayQuery.slang | 38 ++++ 16 files changed, 581 insertions(+), 46 deletions(-) create mode 100644 VisualTests/Tests/RayTracingCompute.cpp create mode 100644 VisualTests/Tests/RayTracingCompute.hpp create mode 100644 resources/VisualTests/Shaders/RayQuery.slang diff --git a/ShockGraph/Resources.cpp b/ShockGraph/Resources.cpp index 45354a3..88ebabc 100644 --- a/ShockGraph/Resources.cpp +++ b/ShockGraph/Resources.cpp @@ -20,8 +20,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#pragma once - #include "Resources.hpp" #include "TaskResourceManager.hpp" #include @@ -166,5 +164,17 @@ namespace PyroshockStudios { SHOCKGRAPH_API TaskSwapChain_::~TaskSwapChain_() { Device()->Destroy(mSwapChain); } + SHOCKGRAPH_API TaskBlas_::TaskBlas_(TaskResourceManager* owner, const TaskBlasInfo& info, BlasId&& blas) + : TaskResource_(owner), mBlas(blas), mInfo(info) { + } + SHOCKGRAPH_API TaskBlas_::~TaskBlas_() { + Device()->Destroy(mBlas); + } + SHOCKGRAPH_API TaskTlas_::TaskTlas_(TaskResourceManager* owner, const TaskTlasInfo& info, TlasId&& tlas) + : TaskResource_(owner), mTlas(tlas), mInfo(info) { + } + SHOCKGRAPH_API TaskTlas_::~TaskTlas_() { + Device()->Destroy(mTlas); + } } // namespace Renderer } // namespace PyroshockStudios \ No newline at end of file diff --git a/ShockGraph/Resources.hpp b/ShockGraph/Resources.hpp index 786e9d9..dbf4f9a 100644 --- a/ShockGraph/Resources.hpp +++ b/ShockGraph/Resources.hpp @@ -33,6 +33,7 @@ #include #include #include +#include #include namespace PyroshockStudios { @@ -335,5 +336,49 @@ namespace PyroshockStudios { }; using TaskSwapChain = SharedRef; using TaskSwapChainRef = TaskSwapChain&; + + struct TaskBlasInfo { + usize size = 0; + eastl::string name = {}; + }; + struct TaskBlas_ final : public TaskResource_ { + SHOCKGRAPH_API TaskBlas_(TaskResourceManager* owner, const TaskBlasInfo& info, BlasId&& blas); + SHOCKGRAPH_API ~TaskBlas_() override; + PYRO_NODISCARD PYRO_FORCEINLINE BlasId Internal() { + return mBlas; + } + PYRO_NODISCARD PYRO_FORCEINLINE const TaskBlasInfo& Info() const { return mInfo; } + + private: + BlasId mBlas = PYRO_NULL_BLAS; + TaskBlasInfo mInfo; + + friend class TaskResourceManager; + friend class TaskGraph; + }; + using TaskBlas = SharedRef; + using TaskBlasRef = TaskBlas&; + + struct TaskTlasInfo { + usize size = 0; + eastl::string name = {}; + }; + struct TaskTlas_ final : public TaskResource_ { + SHOCKGRAPH_API TaskTlas_(TaskResourceManager* owner, const TaskTlasInfo& info, TlasId&& tlas); + SHOCKGRAPH_API ~TaskTlas_() override; + PYRO_NODISCARD PYRO_FORCEINLINE TlasId Internal() { + return mTlas; + } + PYRO_NODISCARD PYRO_FORCEINLINE const TaskTlasInfo& Info() const { return mInfo; } + + private: + TlasId mTlas = PYRO_NULL_TLAS; + TaskTlasInfo mInfo; + + friend class TaskResourceManager; + friend class TaskGraph; + }; + using TaskTlas = SharedRef; + using TaskTlasRef = TaskTlas&; } // namespace Renderer } // namespace PyroshockStudios \ No newline at end of file diff --git a/ShockGraph/Task.cpp b/ShockGraph/Task.cpp index 36fa117..50b8bde 100644 --- a/ShockGraph/Task.cpp +++ b/ShockGraph/Task.cpp @@ -63,6 +63,12 @@ namespace PyroshockStudios { SHOCKGRAPH_API void GenericTask::UseImage(const TaskImageDependencyInfo& info) { mSetupData.imageDepends.emplace_back(info); } + SHOCKGRAPH_API void GenericTask::UseBlas(const TaskBlasDependencyInfo& info) { + mSetupData.blasDepends.emplace_back(info); + } + SHOCKGRAPH_API void GenericTask::UseTlas(const TaskTlasDependencyInfo& info) { + mSetupData.tlasDepends.emplace_back(info); + } SHOCKGRAPH_API void CustomTask::ExecuteTask(TaskCommandList& commandList) { ExecuteTask(commandList.Internal()); } diff --git a/ShockGraph/Task.hpp b/ShockGraph/Task.hpp index 187f19a..2afed89 100644 --- a/ShockGraph/Task.hpp +++ b/ShockGraph/Task.hpp @@ -56,6 +56,20 @@ namespace PyroshockStudios { PYRO_NODISCARD PYRO_FORCEINLINE bool operator==(const TaskImageDependencyInfo&) const = default; PYRO_NODISCARD PYRO_FORCEINLINE bool operator!=(const TaskImageDependencyInfo&) const = default; }; + struct TaskBlasDependencyInfo { + TaskBlas blas; + TaskAccessType access; + + PYRO_NODISCARD PYRO_FORCEINLINE bool operator==(const TaskBlasDependencyInfo&) const = default; + PYRO_NODISCARD PYRO_FORCEINLINE bool operator!=(const TaskBlasDependencyInfo&) const = default; + }; + struct TaskTlasDependencyInfo { + TaskTlas tlas; + TaskAccessType access; + + PYRO_NODISCARD PYRO_FORCEINLINE bool operator==(const TaskTlasDependencyInfo&) const = default; + PYRO_NODISCARD PYRO_FORCEINLINE bool operator!=(const TaskTlasDependencyInfo&) const = default; + }; struct TaskInfo { eastl::string name = {}; @@ -80,6 +94,8 @@ namespace PyroshockStudios { SHOCKGRAPH_API void UseBuffer(const TaskBufferDependencyInfo& info); SHOCKGRAPH_API void UseImage(const TaskImageDependencyInfo& info); + SHOCKGRAPH_API void UseBlas(const TaskBlasDependencyInfo& info); + SHOCKGRAPH_API void UseTlas(const TaskTlasDependencyInfo& info); PYRO_NODISCARD PYRO_FORCEINLINE const TaskInfo& Info() const { return mTaskInfo; @@ -89,6 +105,8 @@ namespace PyroshockStudios { struct GenericSetup { eastl::vector bufferDepends; eastl::vector imageDepends; + eastl::vector blasDepends; + eastl::vector tlasDepends; }; GenericSetup mSetupData = {}; diff --git a/ShockGraph/TaskCommandList.hpp b/ShockGraph/TaskCommandList.hpp index 0b654a0..1465638 100644 --- a/ShockGraph/TaskCommandList.hpp +++ b/ShockGraph/TaskCommandList.hpp @@ -24,6 +24,7 @@ #include "Resources.hpp" #include +#include namespace PyroshockStudios { inline namespace Renderer { @@ -101,6 +102,29 @@ namespace PyroshockStudios { usize indirectBufferOffset = {}; }; + struct TaskBlasBuildInfo { + AccelerationStructureCreateFlags flags = AccelerationStructureCreateFlagBits::NONE; + bool bUpdate = false; + TaskBlas srcBlas = {}; + TaskBlas dstBlas = {}; + eastl::variant, eastl::span> geometries = {}; + Buffer scratchBuffer = PYRO_NULL_BUFFER; + }; + + struct TaskTlasBuildInfo { + AccelerationStructureCreateFlags flags = AccelerationStructureCreateFlagBits::NONE; + bool update = false; + TaskTlas srcTlas = {}; + TaskTlas dstTlas = {}; + TlasInstanceInfo instances = {}; + Buffer scratchBuffer = PYRO_NULL_BUFFER; + }; + + struct TaskBuildAccelerationStructuresInfo { + eastl::span tlasBuildInfos = {}; + eastl::span blasBuildInfos = {}; + }; + class TaskCommandList : DeleteCopy, DeleteMove { public: PYRO_FORCEINLINE void CopyBuffer(const TaskCopyBufferInfo& info) { @@ -217,6 +241,40 @@ namespace PyroshockStudios { }); } + PYRO_FORCEINLINE void BuildAccelerationStructures(const TaskBuildAccelerationStructuresInfo& info) { + eastl::vector tlasBuildInfos = {}; + eastl::vector blasBuildInfos = {}; + + tlasBuildInfos.reserve(info.tlasBuildInfos.size()); + for (const auto& t : info.tlasBuildInfos) { + TlasBuildInfo out = {}; + out.flags = t.flags; + out.update = t.update; + out.srcTlas = t.srcTlas ? t.srcTlas->Internal() : PYRO_NULL_TLAS; + out.dstTlas = t.dstTlas ? t.dstTlas->Internal() : PYRO_NULL_TLAS; + out.instances = t.instances; + out.scratchBuffer = t.scratchBuffer; + tlasBuildInfos.push_back(out); + } + + blasBuildInfos.reserve(info.blasBuildInfos.size()); + for (const auto& b : info.blasBuildInfos) { + BlasBuildInfo out = {}; + out.flags = b.flags; + out.bUpdate = b.bUpdate; + out.srcBlas = b.srcBlas ? b.srcBlas->Internal() : PYRO_NULL_BLAS; + out.dstBlas = b.dstBlas ? b.dstBlas->Internal() : PYRO_NULL_BLAS; + out.geometries = b.geometries; + out.scratchBuffer = b.scratchBuffer; + blasBuildInfos.push_back(out); + } + + mCommandBuffer->BuildAccelerationStructures({ + .tlasBuildInfos = tlasBuildInfos, + .blasBuildInfos = blasBuildInfos, + }); + } + PYRO_FORCEINLINE ICommandBuffer* Internal() { return mCommandBuffer; } @@ -227,7 +285,7 @@ namespace PyroshockStudios { if (!pipeline->mbDirty) return; pipeline->mbDirty = false; - mCommandBuffer->DestroyDeferred(pipeline->mPipeline); + mOwningDevice->Destroy(pipeline->mPipeline, true); pipeline->Recreate(); } diff --git a/ShockGraph/TaskGraph.cpp b/ShockGraph/TaskGraph.cpp index 7431502..5786383 100644 --- a/ShockGraph/TaskGraph.cpp +++ b/ShockGraph/TaskGraph.cpp @@ -302,8 +302,8 @@ namespace PyroshockStudios { ASSERT(false, "NO render targets defined!"); } renderPassInfo.renderArea = { - .width = static_cast(extent.x), - .height = static_cast(extent.y), + .width = static_cast(extent.width), + .height = static_cast(extent.height), }; mAllTaskRefs.push_back(task); mTasks.push_back(new GraphicsTaskExecute(task, eastl::move(renderPassInfo))); @@ -347,10 +347,11 @@ namespace PyroshockStudios { task.UseImage({ .image = writeInfo.image, .access = AccessConsts::BLIT_READ }); }, [this, writeInfo](ICommandBuffer* commandBuffer) { - Image swapImage = writeInfo.swapChain->Internal()->AcquireNextImage(); - if (!swapImage) { + i32 result = writeInfo.swapChain->Internal()->AcquireNextImage(); + if (result == PYRO_SWAPCHAIN_ACQUIRE_FAIL) { return; } + Image swapImage = writeInfo.swapChain->Internal()->GetBackBuffer(result); commandBuffer->ImageBarrier({ .image = swapImage, .srcAccess = AccessConsts::BOTTOM_OF_PIPE_READ, @@ -361,8 +362,22 @@ namespace PyroshockStudios { commandBuffer->BlitImageToImage({ .srcImage = writeInfo.image->Internal(), .dstImage = swapImage, - .srcImageRect = writeInfo.srcRect, - .dstImageRect = writeInfo.dstRect, + .srcImageBox = { + .x = writeInfo.srcRect.x, + .y = writeInfo.srcRect.y, + .z = 0, + .width = writeInfo.srcRect.width, + .height = writeInfo.srcRect.height, + .depth = 1 + }, + .dstImageBox = { + .x = writeInfo.dstRect.x, + .y = writeInfo.dstRect.y, + .z = 0, + .width = writeInfo.dstRect.width, + .height = writeInfo.dstRect.height, + .depth = 1 + }, }); commandBuffer->ImageBarrier({ .image = swapImage, @@ -696,7 +711,7 @@ namespace PyroshockStudios { void TaskGraph::FlushStagingBuffers(ICommandBuffer* commandBuffer) { commandBuffer->BeginLabel({ .labelColor = LabelColor::BLUE, .name = "Flush staging buffers" }); - for (const auto& uploadPair : mResourceManager->mPendingStagingUploads) { + for (auto& uploadPair : mResourceManager->mPendingStagingUploads) { commandBuffer->BufferBarrier({ .buffer = uploadPair.srcBuffer, .srcAccess = AccessConsts::HOST_WRITE, @@ -704,7 +719,7 @@ namespace PyroshockStudios { .srcLayout = BufferLayout::TransferSrc, .dstLayout = BufferLayout::TransferSrc, }); - for (const auto& stagingUpload : uploadPair.uploads) { + for (auto& stagingUpload : uploadPair.uploads) { if (stagingUpload.dstBuffer) { commandBuffer->BufferBarrier({ .buffer = stagingUpload.dstBuffer, @@ -749,7 +764,7 @@ namespace PyroshockStudios { }); } } - commandBuffer->DestroyDeferred(uploadPair.srcBuffer); + mDevice->Destroy(uploadPair.srcBuffer, true); } mResourceManager->mPendingStagingUploads.clear(); diff --git a/ShockGraph/TaskResourceManager.cpp b/ShockGraph/TaskResourceManager.cpp index f8385ac..9660927 100644 --- a/ShockGraph/TaskResourceManager.cpp +++ b/ShockGraph/TaskResourceManager.cpp @@ -64,6 +64,7 @@ namespace PyroshockStudios { ASSERT(mDevice, "Device was not set!"); ASSERT(mFramesInFlight >= 2, "Frames in flight must be at least 2!"); } + SHOCKGRAPH_API TaskResourceManager::~TaskResourceManager() { if (mResources.size() != mTombstones.size()) { Logger::Fatal(mLogStream, "Not all resources have been released before task resource manager destruction! " @@ -71,6 +72,7 @@ namespace PyroshockStudios { } delete mShaderReloadListener; } + SHOCKGRAPH_API TaskBuffer TaskResourceManager::CreatePersistentBuffer(const TaskBufferInfo& info, eastl::span initialData) { BufferUsageFlags extraRequiredFlags = {}; eastl::vector buffersInFlight{}; @@ -84,7 +86,7 @@ namespace PyroshockStudios { for (i32 i = 0; i < mFramesInFlight; ++i) { buffersInFlight[i] = mDevice->CreateBuffer({ .size = info.size, - .usage = BufferUsageFlagBits::TRANSFER_SRC | BufferUsageFlagBits::HOST_WRITE | extraRequiredFlags, + .usage = BufferUsageFlagBits::TRANSFER_SRC | extraRequiredFlags, .initialLayout = info.bReadback ? BufferLayout::TransferDst : BufferLayout::TransferSrc, .allocationDomain = info.bReadback ? MemoryAllocationDomain::HostReadback : MemoryAllocationDomain::HostRandomWrite, .name = info.name + " (In Flight #" + eastl::to_string(i) + ")", @@ -132,6 +134,7 @@ namespace PyroshockStudios { } return retBuffer; } + SHOCKGRAPH_API TaskImage TaskResourceManager::CreatePersistentImage(const TaskImageInfo& info, eastl::span initialData) { ImageUsageFlags extraRequiredFlags = {}; @@ -150,8 +153,8 @@ namespace PyroshockStudios { }); // FIXME, texture arrays/mipmaps! if (!initialData.empty()) { - const u32 rowAlignment = mDevice->GetProperties().bufferImageRowAlignment; - const DeviceSize minReqSize = RHIUtil::GetRequiredStagingSize(info.format, info.size.x, info.size.y, info.size.z, 1); + const u32 rowAlignment = mDevice->Properties().bufferImageRowAlignment; + const DeviceSize minReqSize = RHIUtil::GetRequiredStagingSize(info.format, info.size.width, info.size.height, info.size.depth, 1); ASSERT(minReqSize > 0, "Invalid format for staging upload"); ASSERT(initialData.size_bytes() >= minReqSize, "Initial data is too small in size!"); @@ -163,12 +166,12 @@ namespace PyroshockStudios { .name = info.name + " (Staging Buffer)", }); - const u32 rowWidth = static_cast(minReqSize / info.size.z / info.size.y); - const u32 rowPitch = mDevice->ImageSubresourceRowPitch(image, {}, rowWidth); + const u32 rowWidth = static_cast(minReqSize / info.size.depth / info.size.height); + const u32 rowPitch = mDevice->ImageSubresourceRowPitch(image, rowWidth, {}); u8* dstPtr = mDevice->BufferHostAddress(staging); const u8* srcPtr = initialData.data(); - RHIUtil::CopyAlignedTextureData(srcPtr, dstPtr, rowWidth, info.size.y, info.size.z, rowPitch); + RHIUtil::CopyAlignedTextureData(srcPtr, dstPtr, rowWidth, info.size.height, info.size.depth, rowPitch); StagingUploadPair uploadPair{}; uploadPair.srcBuffer = staging; @@ -189,6 +192,25 @@ namespace PyroshockStudios { } return TaskImage::Create(this, info, eastl::move(image)); } + + SHOCKGRAPH_API TaskBlas TaskResourceManager::CreatePersistentBlas(const TaskBlasInfo& info) { + BlasId blas = mDevice->CreateBlas({ + .size = info.size, + .name = info.name, + }); + + return TaskBlas::Create(this, info, eastl::move(blas)); + } + + SHOCKGRAPH_API TaskTlas TaskResourceManager::CreatePersistentTlas(const TaskTlasInfo& info) { + TlasId tlas = mDevice->CreateTlas({ + .size = info.size, + .name = info.name, + }); + + return TaskTlas::Create(this, info, eastl::move(tlas)); + } + SHOCKGRAPH_API ShaderResourceId TaskResourceManager::DefaultShaderResourceView(TaskImage image) { auto& imageInfo = mDevice->GetImageInfo(image->Internal()); ImageResourceInfo resourceInfo{ @@ -216,6 +238,7 @@ namespace PyroshockStudios { } return mDevice->CreateShaderResource(resourceInfo); } + SHOCKGRAPH_API ShaderResourceId TaskResourceManager::DefaultShaderResourceView(TaskBuffer buffer) { auto& bufferInfo = mDevice->GetBufferInfo(buffer->Internal()); BufferResourceInfo resourceInfo{ @@ -238,6 +261,7 @@ namespace PyroshockStudios { }); return TaskColorTarget::Create(this, info, eastl::move(renderTarget)); } + SHOCKGRAPH_API TaskDepthStencilTarget TaskResourceManager::CreateDepthStencilTarget(const TaskDepthStencilTargetInfo& info) { ASSERT(info.image, "No Image defined!"); RenderTarget renderTarget = mDevice->CreateRenderTarget({ @@ -248,12 +272,14 @@ namespace PyroshockStudios { }); return TaskDepthStencilTarget::Create(this, info, eastl::move(renderTarget)); } + SHOCKGRAPH_API ShaderResourceId TaskResourceManager::CreateShaderResourceView(const TaskBufferResourceInfo& info) { return mDevice->CreateShaderResource(BufferResourceInfo{ .buffer = info.buffer->Internal(), .region = info.region, }); } + SHOCKGRAPH_API ShaderResourceId TaskResourceManager::CreateShaderResourceView(const TaskImageResourceInfo& info) { return mDevice->CreateShaderResource(ImageResourceInfo{ .image = info.image->Internal(), @@ -262,12 +288,14 @@ namespace PyroshockStudios { .format = info.format, }); } + SHOCKGRAPH_API UnorderedAccessId TaskResourceManager::CreateUnorderedAccessView(const TaskBufferResourceInfo& info) { return mDevice->CreateUnorderedAccess(BufferResourceInfo{ .buffer = info.buffer->Internal(), .region = info.region, }); } + SHOCKGRAPH_API UnorderedAccessId TaskResourceManager::CreateUnorderedAccessView(const TaskImageResourceInfo& info) { return mDevice->CreateUnorderedAccess(ImageResourceInfo{ .image = info.image->Internal(), @@ -276,15 +304,19 @@ namespace PyroshockStudios { .format = info.format, }); } + SHOCKGRAPH_API SamplerId TaskResourceManager::CreateSampler(const TaskSamplerInfo& info) { return mDevice->CreateSampler(info); } + SHOCKGRAPH_API void TaskResourceManager::ReleaseShaderResourceView(ShaderResourceId& id) { mDevice->Destroy(id); } + SHOCKGRAPH_API void TaskResourceManager::ReleaseUnorderedAccessView(UnorderedAccessId& id) { mDevice->Destroy(id); } + SHOCKGRAPH_API void TaskResourceManager::ReleaseSampler(SamplerId& id) { mDevice->Destroy(id); } @@ -325,12 +357,14 @@ namespace PyroshockStudios { pipeline->Recreate(); return pipeline; } + SHOCKGRAPH_API TaskComputePipeline TaskResourceManager::CreateComputePipeline(const TaskComputePipelineInfo& info, const TaskShaderInfo& shader) { TaskComputePipeline pipeline = TaskComputePipeline::Create(this, info, shader); shader.program->mUsedByResources.emplace_back(pipeline.Get()); pipeline->Recreate(); return pipeline; } + SHOCKGRAPH_API TaskSwapChain TaskResourceManager::CreateSwapChain(const TaskSwapChainInfo& info) { SwapChainFormat format = {}; switch (info.format) { @@ -357,7 +391,7 @@ namespace PyroshockStudios { .nativeInstance = info.nativeInstance, #endif .format = format, - .presentMode = info.vsync ? PresentMode::VSync : PresentMode::LowLatency, + .presentMode = info.vsync ? SwapChainPresentMode::VSync : SwapChainPresentMode::LowLatency, .bufferCount = mFramesInFlight, .imageUsage = info.imageUsage, #ifdef SHOCKGRAPH_USE_PYRO_PLATFORM @@ -369,12 +403,15 @@ namespace PyroshockStudios { }); return TaskSwapChain::Create(this, info, eastl::move(swapChain)); } + void TaskResourceManager::SetFramesInFlight(u32 newFramesInFlight) { ASSERT(false, "Not implemented"); } + IShaderReloadListener* TaskResourceManager::GetShaderReloadListener() { return mShaderReloadListener; } + void TaskResourceManager::RegisterResource(TaskResource_* resource) { resource->mOwner = this; if (mTombstones.empty()) { @@ -386,6 +423,7 @@ namespace PyroshockStudios { mResources[resource->mId] = resource; } } + void TaskResourceManager::ReleaseResource(TaskResource_* resource) { u32 slot = resource->GetId(); ASSERT(mResources.size() > slot, "Bad slot!"); @@ -394,6 +432,7 @@ namespace PyroshockStudios { mTombstones.emplace_back(slot); mResources[slot] = {}; } + void TaskResourceManager::ReleaseBufferResource(TaskBuffer_* resource) { if (resource->Info().bDynamic) { auto it = eastl::find(mDynamicBuffers.begin(), mDynamicBuffers.end(), resource); @@ -410,6 +449,7 @@ namespace PyroshockStudios { } } } + void TaskResourceManager::ReleaseImageResource(TaskImage_* resource) { for (auto& staging : mPendingStagingUploads) { for (i32 i = 0; i < staging.uploads.size(); ++i) { diff --git a/ShockGraph/TaskResourceManager.hpp b/ShockGraph/TaskResourceManager.hpp index 689de97..29cf264 100644 --- a/ShockGraph/TaskResourceManager.hpp +++ b/ShockGraph/TaskResourceManager.hpp @@ -58,6 +58,8 @@ namespace PyroshockStudios { PYRO_NODISCARD SHOCKGRAPH_API TaskBuffer CreatePersistentBuffer(const TaskBufferInfo& info, eastl::span initialData = {}); PYRO_NODISCARD SHOCKGRAPH_API TaskImage CreatePersistentImage(const TaskImageInfo& info, eastl::span initialData = {}); + PYRO_NODISCARD SHOCKGRAPH_API TaskBlas CreatePersistentBlas(const TaskBlasInfo& info); + PYRO_NODISCARD SHOCKGRAPH_API TaskTlas CreatePersistentTlas(const TaskTlasInfo& info); PYRO_NODISCARD SHOCKGRAPH_API ShaderResourceId DefaultShaderResourceView(TaskImage image); PYRO_NODISCARD SHOCKGRAPH_API ShaderResourceId DefaultShaderResourceView(TaskBuffer image); diff --git a/VisualTests/App.cpp b/VisualTests/App.cpp index d739f5e..2be710e 100644 --- a/VisualTests/App.cpp +++ b/VisualTests/App.cpp @@ -244,6 +244,14 @@ namespace VisualTests { CreateRHI(); return; } + + if(strcmp(mRHIManager->GetAttachedRHIInfo().info.shorthand, "dx12") == 0) { + mRHIManager->GetRHIDevice()->SetShaderModel(0x65); + } + + if(strcmp(mRHIManager->GetAttachedRHIInfo().info.shorthand, "vk13") == 0) { + mRHIManager->GetRHIDevice()->SetShaderModel(0x14); + } } else { Logger::Fatal(gRHILoaderSink, "Failed to find a suitable RHI!"); } @@ -295,7 +303,7 @@ namespace VisualTests { mCurrentTestTasks.emplace_back(eastl::unique_ptr(task)); } TaskImage toComposite = ActiveTest()->GetCompositeImageTaskGraph(); - Rect2D srcRect = Rect2D::Cut({ toComposite->Info().size.x, toComposite->Info().size.y }); + Rect2D srcRect = Rect2D::Cut({ toComposite->Info().size.width, toComposite->Info().size.height }); Rect2D dstRect = Rect2D::Cut({ mActiveWindow->GetSize().width, mActiveWindow->GetSize().height }); if (mRHIManager->GetAttachedRHI()->Properties().viewportConvention == RHIViewportConvention::LeftHanded_OriginTopLeft) { dstRect.y = dstRect.height; diff --git a/VisualTests/Tests/AlphaToCoverage.cpp b/VisualTests/Tests/AlphaToCoverage.cpp index 80c6627..c916b34 100644 --- a/VisualTests/Tests/AlphaToCoverage.cpp +++ b/VisualTests/Tests/AlphaToCoverage.cpp @@ -29,7 +29,7 @@ namespace VisualTests { static const eastl::array gOffset1 = { 0.5, 0.0f }; void AlphaToCoverage::CreateResources(const CreateResourceInfo& info) { - RasterizationSamples sampleCount = info.resourceManager.GetInternalDevice()->GetProperties().maxRenderTargetSamples; + RasterizationSamples sampleCount = info.resourceManager.GetInternalDevice()->Properties().msaaSupportColorTarget; image = info.resourceManager.CreatePersistentImage({ .format = Format::RGBA8Unorm, .size = { info.displayInfo.width, info.displayInfo.height }, diff --git a/VisualTests/Tests/BlitImage.cpp b/VisualTests/Tests/BlitImage.cpp index 95e4c7d..dbc9bbb 100644 --- a/VisualTests/Tests/BlitImage.cpp +++ b/VisualTests/Tests/BlitImage.cpp @@ -94,8 +94,8 @@ namespace VisualTests { // Destination layout: 2x2 grid in dst image // Each quadrant will test a different blit configuration - i32 halfW = dstDim.x / 2; - i32 halfH = dstDim.y / 2; + i32 halfW = dstDim.width / 2; + i32 halfH = dstDim.height / 2; // Configurations to test struct TestConfig { @@ -108,34 +108,48 @@ namespace VisualTests { // 0: "Clear" blit. Blit from empty src to full dst tests.push_back({ { 0, 0, 1, 1 }, - { 0, 0, (i32)dstDim.x, (i32)dstDim.y }, + { 0, 0, (i32)dstDim.width, (i32)dstDim.height }, Filter::Nearest }); // 1: Full source -> top-left quadrant, nearest filter - tests.push_back({ { 0, 0, (i32)srcDim.x, (i32)srcDim.y }, + tests.push_back({ { 0, 0, (i32)srcDim.width, (i32)srcDim.height }, { 0, halfH, halfW, halfH }, Filter::Nearest }); // 2: Center quarter of source -> top-right quadrant, linear filter - tests.push_back({ { (i32)srcDim.x / 4, (i32)srcDim.y / 4, - (i32)srcDim.x / 2, (i32)srcDim.y / 2 }, + tests.push_back({ { (i32)srcDim.width / 4, (i32)srcDim.height / 4, + (i32)srcDim.width / 2, (i32)srcDim.height / 2 }, { halfW, halfH, halfW, halfH }, Filter::Linear }); // 3: Flipped X source -> bottom-left quadrant, nearest filter - tests.push_back({ { (i32)srcDim.x, 0, -(i32)srcDim.x, (i32)srcDim.y }, + tests.push_back({ { (i32)srcDim.width, 0, -(i32)srcDim.width, (i32)srcDim.height }, { 0, 0, halfW, halfH }, Filter::Nearest }); // 4: Flipped Y + scaled -> bottom-right quadrant, linear filter - tests.push_back({ { 0, (i32)srcDim.y, (i32)srcDim.x, -(i32)srcDim.y }, + tests.push_back({ { 0, (i32)srcDim.height, (i32)srcDim.width, -(i32)srcDim.height }, { halfW + halfW / 4, halfH / 4, halfW / 2, halfH / 2 }, Filter::Linear }); // Now run all blits for (auto& t : tests) { - blitInfo.srcImageRect = t.srcRect; - blitInfo.dstImageRect = t.dstRect; + blitInfo.srcImageBox = { + .x = t.srcRect.x, + .y = t.srcRect.y, + .z = 0, + .width = t.srcRect.width, + .height = t.srcRect.height, + .depth = 1 + }; + blitInfo.dstImageBox = { + .x = t.dstRect.x, + .y = t.dstRect.y, + .z = 0, + .width = t.dstRect.width, + .height = t.dstRect.height, + .depth = 1 + }; blitInfo.filter = t.filter; commands->BlitImageToImage(blitInfo); diff --git a/VisualTests/Tests/MSAA.cpp b/VisualTests/Tests/MSAA.cpp index 134c927..c24ff71 100644 --- a/VisualTests/Tests/MSAA.cpp +++ b/VisualTests/Tests/MSAA.cpp @@ -26,7 +26,7 @@ namespace VisualTests { void MSAA::CreateResources(const CreateResourceInfo& info) { - RasterizationSamples sampleCount = info.resourceManager.GetInternalDevice()->GetProperties().maxRenderTargetSamples; + RasterizationSamples sampleCount = info.resourceManager.GetInternalDevice()->Properties().msaaSupportColorTarget; image = info.resourceManager.CreatePersistentImage({ .format = Format::RGBA8Unorm, .size = { info.displayInfo.width, info.displayInfo.height }, @@ -97,15 +97,15 @@ namespace VisualTests { [this](TaskCommandList& commands) { commands.SetViewport({ .x = 0.0f, - .y = static_cast(image->Info().size.y / 4), - .width = static_cast(image->Info().size.x / 2), - .height = static_cast(image->Info().size.y / 2), + .y = static_cast(image->Info().size.height / 4), + .width = static_cast(image->Info().size.width / 2), + .height = static_cast(image->Info().size.height / 2), }); commands.SetScissor({ .x = 0, - .y = static_cast(image->Info().size.y / 4), - .width = static_cast(image->Info().size.x / 2), - .height = static_cast(image->Info().size.y / 2), + .y = static_cast(image->Info().size.height / 4), + .width = static_cast(image->Info().size.width / 2), + .height = static_cast(image->Info().size.height / 2), }); commands.SetRasterPipeline(pipelineMSAA); commands.Draw({ .vertexCount = 3 }); @@ -119,16 +119,16 @@ namespace VisualTests { }, [this](TaskCommandList& commands) { commands.SetViewport({ - .x = static_cast(image->Info().size.x / 2), - .y = static_cast(image->Info().size.y / 4), - .width = static_cast(image->Info().size.x / 2), - .height = static_cast(image->Info().size.y / 2), + .x = static_cast(image->Info().size.width / 2), + .y = static_cast(image->Info().size.height / 4), + .width = static_cast(image->Info().size.width / 2), + .height = static_cast(image->Info().size.height / 2), }); commands.SetScissor({ - .x = static_cast(image->Info().size.x / 2), - .y = static_cast(image->Info().size.y / 4), - .width = static_cast(image->Info().size.x / 2), - .height = static_cast(image->Info().size.y / 2), + .x = static_cast(image->Info().size.width / 2), + .y = static_cast(image->Info().size.height / 4), + .width = static_cast(image->Info().size.width / 2), + .height = static_cast(image->Info().size.height / 2), }); commands.SetRasterPipeline(pipeline); commands.Draw({ .vertexCount = 3 }); diff --git a/VisualTests/Tests/RayTracingCompute.cpp b/VisualTests/Tests/RayTracingCompute.cpp new file mode 100644 index 0000000..fb69ea6 --- /dev/null +++ b/VisualTests/Tests/RayTracingCompute.cpp @@ -0,0 +1,223 @@ +// MIT License +// +// Copyright (c) 2025 Pyroshock Studios +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "RayTracingCompute.hpp" + +namespace VisualTests { + + void RayTracingCompute::CreateResources(const CreateResourceInfo& info) { + image = info.resourceManager.CreatePersistentImage({ + .format = Format::RGBA8Unorm, + .size = { info.displayInfo.width, info.displayInfo.height }, + .usage = ImageUsageFlagBits::UNORDERED_ACCESS | ImageUsageFlagBits::TRANSFER_SRC | ImageUsageFlagBits::BLIT_SRC, + .name = "Ray-Query Compute Image", + }); + + imageUav = info.resourceManager.CreateUnorderedAccessView({ .image = image }); + + csh = info.shaderCompiler.CompileShaderFromFile("resources/VisualTests/Shaders/RayQuery.slang", + { .stage = ShaderStage::Compute, .entryPoint = "computeMain", .name = "RayQuery Compute" }); + + computePipeline = info.resourceManager.CreateComputePipeline({ .name = "RayQuery Compute Pipeline" }, { .program = csh }); + + // --- Build geometry buffers for BLAS/TLAS + struct SimpleVertex { f32 x, y, z; }; + + const SimpleVertex vertices[] = { { 3.f, 3.f, 4.f }, { -3.f, 3.f, 4.f }, { 0.f, -3.f, 4.f }, { 0.f, 0.f, 4.f } }; + const u32 indices[] = { 0, 1, 2, 1, 2, 3 }; + + // Create vertex/index buffers with initial data + vertexBuffer = info.resourceManager.CreatePersistentBuffer({ + .size = sizeof(vertices), + .usage = BufferUsageFlagBits::BLAS_GEOMETRY_BUFFER, + .bCpuVisible = true, + .name = "RT Vertices", + }, eastl::span(reinterpret_cast(vertices), sizeof(vertices))); + + indexBuffer = info.resourceManager.CreatePersistentBuffer({ + .size = sizeof(indices), + .usage = BufferUsageFlagBits::BLAS_GEOMETRY_BUFFER, + .bCpuVisible = true, + .name = "RT Indices", + }, eastl::span(reinterpret_cast(indices), sizeof(indices))); + + // Get the internal device to query size requirements + IDevice* device = info.resourceManager.GetInternalDevice(); + + // Prepare RHI Blas geometry info + BlasTriangleGeometryInfo triGeo{}; + triGeo.vertexFormat = Format::RGB32Sfloat; + triGeo.indexType = IndexType::Uint32; + triGeo.vertexBuffer = vertexBuffer->Internal(); + triGeo.indexBuffer = indexBuffer->Internal(); + triGeo.vertexStride = sizeof(SimpleVertex); + triGeo.vertexCount = 4; + triGeo.indexCount = 6; + + eastl::array geoArray = { triGeo }; + BlasBuildInfo blasBuildInfo = { .geometries = geoArray }; + + AccelerationStructureBuildSizesInfo blasSizeInfo = device->BlasSizeRequirements(blasBuildInfo); + + // Create BLAS resource + blas = info.resourceManager.CreatePersistentBlas({ .size = blasSizeInfo.accelerationStructureSize, .name = "RT Blas" }); + + // Create scratch buffers for BLAS and TLAS + blasScratchBuffer = info.resourceManager.CreatePersistentBuffer({ + .size = blasSizeInfo.buildScratchSize, + .usage = BufferUsageFlagBits::ACCELERATION_STRUCTURE_SCRATCH_BUFFER, + .name = "RT Blas Scratch", + }); + + // Instance buffer for TLAS + BlasInstanceData instanceData{}; + instanceData.transform = Transform::IDENTITY; + instanceData.instanceCustomIndex = 0; + instanceData.mask = 0xFF; + instanceData.instanceShaderBindingTableRecordOffset = 0; + instanceData.flags = 0; + instanceData.blasAddress = device->BlasInstanceAddress(blas->Internal()); + + instanceBuffer = info.resourceManager.CreatePersistentBuffer({ + .size = sizeof(BlasInstanceData), + .usage = BufferUsageFlagBits::BLAS_INSTANCE_BUFFER, + .bCpuVisible = true, + .name = "RT Instance Buffer", + }, eastl::span(reinterpret_cast(&instanceData), sizeof(instanceData))); + + // TLAS size requirements + TlasInstanceInfo tlasInstanceInfo = { .data = instanceBuffer->Internal(), .count = 1 }; + TlasBuildInfo tlasBuildInfo = { .instances = tlasInstanceInfo }; + AccelerationStructureBuildSizesInfo tlasSizeInfo = device->TlasSizeRequirements(tlasBuildInfo); + + // Create TLAS and scratch + tlas = info.resourceManager.CreatePersistentTlas({ .size = tlasSizeInfo.accelerationStructureSize, .name = "RT Tlas" }); + tlasScratchBuffer = info.resourceManager.CreatePersistentBuffer({ + .size = tlasSizeInfo.buildScratchSize, + .usage = BufferUsageFlagBits::ACCELERATION_STRUCTURE_SCRATCH_BUFFER, + .name = "RT Tlas Scratch", + }); + } + + + void RayTracingCompute::ReleaseResources(const ReleaseResourceInfo& info) { + info.resourceManager.ReleaseUnorderedAccessView(imageUav); + image = {}; + csh = {}; + computePipeline = {}; + } + + eastl::span RayTracingCompute::CreateTasks() { + // Task 0: Build BLAS/TLAS + GenericTask* buildTask = new CustomCallbackTask( + { .name = "Build Acceleration Structures", .color = LabelColor::YELLOW }, + [this](CustomTask& task) { + // declare buffer usage so task graph orders correctly + task.UseBuffer({ .buffer = vertexBuffer, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ_WRITE }); + task.UseBuffer({ .buffer = indexBuffer, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ_WRITE }); + task.UseBuffer({ .buffer = instanceBuffer, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ_WRITE }); + task.UseBuffer({ .buffer = blasScratchBuffer, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ_WRITE }); + task.UseBuffer({ .buffer = tlasScratchBuffer, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ_WRITE }); + task.UseTlas({ .tlas = tlas, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ_WRITE }); + task.UseImage({ .image = image, .access = AccessConsts::BLIT_WRITE }); // this is retarded fix + }, + [this](ICommandBuffer* commands) { + // Prepare RHI build infos + BlasTriangleGeometryInfo triGeo{}; + triGeo.vertexFormat = Format::RGB32Sfloat; + triGeo.indexType = IndexType::Uint32; + triGeo.vertexBuffer = vertexBuffer->Internal(); + triGeo.indexBuffer = indexBuffer->Internal(); + triGeo.vertexStride = sizeof(float) * 3; + triGeo.vertexCount = 3; + triGeo.indexCount = 3; + + eastl::array geoArray = { triGeo }; + BlasBuildInfo blasBuildInfo = { .geometries = geoArray }; + blasBuildInfo.dstBlas = blas->Internal(); + blasBuildInfo.scratchBuffer = blasScratchBuffer->Internal(); + + TlasInstanceInfo tlasInstanceInfo = { .data = instanceBuffer->Internal(), .count = 1 }; + TlasBuildInfo tlasBuildInfo = { .instances = tlasInstanceInfo }; + tlasBuildInfo.dstTlas = tlas->Internal(); + tlasBuildInfo.scratchBuffer = tlasScratchBuffer->Internal(); + + BuildAccelerationStructuresInfo buildAllInfo = { + .tlasBuildInfos = eastl::span(&tlasBuildInfo, 1), + .blasBuildInfos = eastl::span(&blasBuildInfo, 1), + }; + + commands->BuildAccelerationStructures(buildAllInfo); + }, + TaskType::Transfer); + + // Task 1: Dispatch compute shader which performs ray queries + GenericTask* dispatchTask = new ComputeCallbackTask( + { .name = "RayQuery Compute Dispatch", .color = LabelColor::YELLOW }, + [this](ComputeTask& task) { + task.UseImage({ .image = image, .access = AccessConsts::COMPUTE_SHADER_WRITE }); + task.UseTlas({ .tlas = tlas, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ }); + }, + [this](TaskCommandList& commands) { + commands.SetComputePipeline(computePipeline); + // push TLAS index so shader can index the descriptor array + u32 tlasIndex = tlas->Internal().index; + commands.PushConstant(tlasIndex); + commands.SetUnorderedAccessView({ .slot = 0, .view = imageUav }); + // dispatch in 8x8 workgroups + Extent3D dim = image->Info().size; + u32 gx = (dim.width + 7) / 8; + u32 gy = (dim.height + 7) / 8; + commands.Dispatch({ .x = gx, .y = gy }); + }); + + // Task 2: Blit compute image to a full-size blit image that will be presented + // GenericTask* blitTask = new CustomCallbackTask( + // { .name = "Blit To Swapchain Image", .color = LabelColor::YELLOW }, + // [this](CustomTask& task) { + // task.UseImage({ .image = image, + // .access = AccessConsts::BLIT_READ }); + // task.UseImage({ .image = blitImage, + // .access = AccessConsts::BLIT_WRITE }); + // }, + // [this](ICommandBuffer* commands) { + // BlitImageToImageInfo blitInfo{}; + // blitInfo.srcImage = image->Internal(); + // blitInfo.dstImage = blitImage->Internal(); + + // Extent3D srcDim = image->Info().size; + // Extent3D dstDim = blitImage->Info().size; + + // blitInfo.srcImageBox = Box3D::Cut({ srcDim.width, srcDim.height, 1 }); + // blitInfo.dstImageBox = Box3D::Cut({ dstDim.width, dstDim.height, 1 }); + // blitInfo.filter = Filter::Nearest; + + // commands->BlitImageToImage(blitInfo); + // }, + // TaskType::Graphics); + + tasks = { buildTask, dispatchTask }; + + return tasks; + } + +} // namespace VisualTests diff --git a/VisualTests/Tests/RayTracingCompute.hpp b/VisualTests/Tests/RayTracingCompute.hpp new file mode 100644 index 0000000..f26d3f3 --- /dev/null +++ b/VisualTests/Tests/RayTracingCompute.hpp @@ -0,0 +1,56 @@ +// MIT License +// +// Copyright (c) 2025 Pyroshock Studios +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once +#include + + +namespace VisualTests { + class RayTracingCompute : public IVisualTest, DeleteCopy, DeleteMove { + eastl::string Title() const override { return "Ray Query Compute"; } + + void CreateResources(const CreateResourceInfo& info) override; + void ReleaseResources(const ReleaseResourceInfo& info) override; + eastl::span CreateTasks() override; + + bool UseTaskGraph() const override { return true; } + TaskImage GetCompositeImageTaskGraph() override { return image; } + Image GetCompositeImageRaw() override { return image->Internal(); } + + private: + TaskImage image; + UnorderedAccessId imageUav; + TaskShader csh; + TaskComputePipeline computePipeline; + + // Acceleration structure resources + TaskBuffer vertexBuffer; + TaskBuffer indexBuffer; + TaskBuffer instanceBuffer; + TaskBuffer blasScratchBuffer; + TaskBuffer tlasScratchBuffer; + TaskBlas blas; + TaskTlas tlas; + + eastl::vector tasks = {}; + }; +} // namespace VisualTests diff --git a/VisualTests/main.cpp b/VisualTests/main.cpp index a044ed7..e4066c8 100644 --- a/VisualTests/main.cpp +++ b/VisualTests/main.cpp @@ -32,6 +32,7 @@ #include "Tests/AlphaToCoverage.hpp" #include "Tests/BlitImage.hpp" #include "Tests/ComputeUAV.hpp" +#include "Tests/RayTracingCompute.hpp" #include "Tests/DrawIndirect.hpp" #include "Tests/GeometryShader.hpp" #include "Tests/HelloTexture.hpp" @@ -84,6 +85,7 @@ int main(i32 argc, char** argv) { app->RegisterTest(); app->RegisterTest(); app->RegisterTest(); + app->RegisterTest(); app->RegisterTest(); app->RegisterTest(); diff --git a/resources/VisualTests/Shaders/RayQuery.slang b/resources/VisualTests/Shaders/RayQuery.slang new file mode 100644 index 0000000..87e7e51 --- /dev/null +++ b/resources/VisualTests/Shaders/RayQuery.slang @@ -0,0 +1,38 @@ +#include +#include + +// Bind an array of TLAS handles at t0 (space1). The runtime maps created TLAS into this array. +[[vk::binding(3, 0)]] RaytracingAccelerationStructure gTlas[] : register(t0, space1); +PYRO_BIND_UNORDERED_ACCESS_TEXTURE_2D(0, float4, gOutput); + +[[push_constant]] cbuffer pyro_PushConstant : register(b13, space0) { uint tlasIndex; }; + +[numthreads(8,8,1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) { + RayQuery query; + + int width, height; + gOutput.GetDimensions(width, height); + + RayDesc ray; + float2 uv = (dispatchThreadID.xy + 0.5) / float2(width, height); + float3 origin = float3(uv * 2.0 - 1.0, -1.0); + float3 direction = float3(0.0, 0.0, 1.0); + ray.Origin = origin; + ray.Direction = direction; + ray.TMin = 0.001; + ray.TMax = 1000.0; + + query.TraceRayInline( + gTlas[tlasIndex], + RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH, + 0xFF, + ray + ); + + uint hit = 0; + if (query.CommittedStatus() == COMMITTED_TRIANGLE_HIT) { + hit = 1; + } + gOutput[dispatchThreadID.xy] = hit == 1 ? float4(1.0, 0.0, 0.0, 1.0) : float4(0.0, 0.0, 0.0, 1.0); +} From 13ee1249825b686414e441fdad8050cefd95f9e4 Mon Sep 17 00:00:00 2001 From: ZilverBlade <90721817+ZilverBlade@users.noreply.github.com> Date: Sat, 15 Nov 2025 01:47:24 +0100 Subject: [PATCH 2/4] im gonna crash out --- .gitmodules | 7 +- ShockGraph/Resources.cpp | 11 +- ShockGraph/Resources.hpp | 17 +- ShockGraph/Task.cpp | 7 +- ShockGraph/Task.hpp | 21 +- ShockGraph/TaskGraph.cpp | 199 +++++++++------- ShockGraph/TaskGraph.hpp | 14 +- ShockGraph/TaskResourceManager.cpp | 40 ++-- VisualTests/App.cpp | 6 + VisualTests/ShaderCompiler.cpp | 6 +- VisualTests/Tests/AlphaToCoverage.cpp | 8 +- VisualTests/Tests/ComputeUAV.cpp | 2 + VisualTests/Tests/DrawIndirect.cpp | 2 + VisualTests/Tests/IndexBuffer.cpp | 2 + VisualTests/Tests/InstanceBuffer.cpp | 2 + VisualTests/Tests/MSAA.cpp | 8 +- ...TracingCompute.cpp => RayQueryCompute.cpp} | 150 ++++++------ ...TracingCompute.hpp => RayQueryCompute.hpp} | 10 +- VisualTests/Tests/RayQueryPixel.cpp | 213 ++++++++++++++++++ VisualTests/Tests/RayQueryPixel.hpp | 57 +++++ VisualTests/Tests/UniformBuffer.cpp | 2 +- VisualTests/Tests/UpdateBuffer.cpp | 3 +- VisualTests/Tests/VertexBuffer.cpp | 1 + VisualTests/main.cpp | 6 +- .../Include/Common/DescriptorIndexing.slang | 28 +-- resources/VisualTests/Shaders/RayQuery.slang | 80 +++++-- .../VisualTests/Shaders/RayQueryCompute.slang | 47 ++++ vendor/PyroRHI | 2 +- 28 files changed, 702 insertions(+), 249 deletions(-) rename VisualTests/Tests/{RayTracingCompute.cpp => RayQueryCompute.cpp} (66%) rename VisualTests/Tests/{RayTracingCompute.hpp => RayQueryCompute.hpp} (87%) create mode 100644 VisualTests/Tests/RayQueryPixel.cpp create mode 100644 VisualTests/Tests/RayQueryPixel.hpp create mode 100644 resources/VisualTests/Shaders/RayQueryCompute.slang diff --git a/.gitmodules b/.gitmodules index 227f6bd..8622c57 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,7 @@ -[submodule "vendor/PyroRHI"] - path = vendor/PyroRHI - url = https://github.com/PyroshockStudios/PyroRHI.git [submodule "vendor/PyroPlatform"] path = vendor/PyroPlatform url = https://github.com/PyroshockStudios/PyroPlatform.git +[submodule "vendor/PyroRHI"] + path = vendor/PyroRHI + url = https://github.com/PyroshockStudios/PyroRHI.git + branch = ray_tracing_fixes diff --git a/ShockGraph/Resources.cpp b/ShockGraph/Resources.cpp index 88ebabc..c5fee85 100644 --- a/ShockGraph/Resources.cpp +++ b/ShockGraph/Resources.cpp @@ -118,7 +118,7 @@ namespace PyroshockStudios { mShader.program->RemoveReference(this); Device()->Destroy(mPipeline); } - void TaskComputePipeline_::Recreate() { + void TaskComputePipeline_::Recreate() { ShaderInfo copyShader; copyShader.program = mShader.program->Program().bytecode; copyShader.specializationConstants = mShader.specializationConstants; @@ -130,7 +130,11 @@ namespace PyroshockStudios { } SHOCKGRAPH_API TaskBuffer_::~TaskBuffer_() { Owner()->ReleaseBufferResource(this); - Device()->Destroy(mBuffer); + if (this->mInfo.mode == TaskBufferMode::HostDynamic || this->mInfo.mode == TaskBufferMode::Readback) { + // Do not destroy mBuffer as it is the same stuff as in mInFlightBuffers! + } else { + Device()->Destroy(mBuffer); + } for (auto& buffer : mInFlightBuffers) { Device()->Destroy(buffer); } @@ -170,6 +174,9 @@ namespace PyroshockStudios { SHOCKGRAPH_API TaskBlas_::~TaskBlas_() { Device()->Destroy(mBlas); } + PYRO_NODISCARD BlasAddress TaskBlas_::InstanceAddress() { + return Device()->BlasInstanceAddress(mBlas); + } SHOCKGRAPH_API TaskTlas_::TaskTlas_(TaskResourceManager* owner, const TaskTlasInfo& info, TlasId&& tlas) : TaskResource_(owner), mTlas(tlas), mInfo(info) { } diff --git a/ShockGraph/Resources.hpp b/ShockGraph/Resources.hpp index dbf4f9a..ffe7c8c 100644 --- a/ShockGraph/Resources.hpp +++ b/ShockGraph/Resources.hpp @@ -27,13 +27,13 @@ #ifdef SHOCKGRAPH_USE_PYRO_PLATFORM #include #endif +#include #include #include #include #include #include #include -#include #include namespace PyroshockStudios { @@ -184,15 +184,17 @@ namespace PyroshockStudios { using TaskComputePipelineRef = TaskComputePipeline&; + enum struct TaskBufferMode : u32 { + Default = 0, ///< Stored on Device, not accessible from CPU. + Dynamic = 1, ///< Stored on Device, with CPU write access, but GPU read-only. Optimised for fast GPU access, but writes may be slower. + HostDynamic = 2, ///< Stored on Host memory, with CPU read/write access, but GPU read-only. Optimised for fast CPU access, but reads may be slower. + Readback = 3, ///< Stored on Host memory, with CPU read access, but GPU write-only. + }; + struct TaskBufferInfo { usize size = 0; BufferUsageFlags usage = {}; - // Buffer is stored on CPU visible memory - bool bCpuVisible = false; - // Buffer can be read from CPU - bool bReadback = false; - // Buffer is reliably accessible between CPU and GPU - bool bDynamic = false; + TaskBufferMode mode = TaskBufferMode::Default; eastl::string name = {}; }; struct TaskBuffer_ final : public TaskResource_ { @@ -348,6 +350,7 @@ namespace PyroshockStudios { return mBlas; } PYRO_NODISCARD PYRO_FORCEINLINE const TaskBlasInfo& Info() const { return mInfo; } + PYRO_NODISCARD BlasAddress InstanceAddress(); private: BlasId mBlas = PYRO_NULL_BLAS; diff --git a/ShockGraph/Task.cpp b/ShockGraph/Task.cpp index 50b8bde..780465d 100644 --- a/ShockGraph/Task.cpp +++ b/ShockGraph/Task.cpp @@ -63,11 +63,8 @@ namespace PyroshockStudios { SHOCKGRAPH_API void GenericTask::UseImage(const TaskImageDependencyInfo& info) { mSetupData.imageDepends.emplace_back(info); } - SHOCKGRAPH_API void GenericTask::UseBlas(const TaskBlasDependencyInfo& info) { - mSetupData.blasDepends.emplace_back(info); - } - SHOCKGRAPH_API void GenericTask::UseTlas(const TaskTlasDependencyInfo& info) { - mSetupData.tlasDepends.emplace_back(info); + SHOCKGRAPH_API void GenericTask::UseAccelerationStructure(const TaskAccelerationStructureDependencyInfo& info) { + mSetupData.accelerationStructureDepends.emplace_back(info); } SHOCKGRAPH_API void CustomTask::ExecuteTask(TaskCommandList& commandList) { ExecuteTask(commandList.Internal()); diff --git a/ShockGraph/Task.hpp b/ShockGraph/Task.hpp index 2afed89..059a146 100644 --- a/ShockGraph/Task.hpp +++ b/ShockGraph/Task.hpp @@ -56,19 +56,12 @@ namespace PyroshockStudios { PYRO_NODISCARD PYRO_FORCEINLINE bool operator==(const TaskImageDependencyInfo&) const = default; PYRO_NODISCARD PYRO_FORCEINLINE bool operator!=(const TaskImageDependencyInfo&) const = default; }; - struct TaskBlasDependencyInfo { - TaskBlas blas; + struct TaskAccelerationStructureDependencyInfo { + eastl::variant accelerationStructure; TaskAccessType access; - PYRO_NODISCARD PYRO_FORCEINLINE bool operator==(const TaskBlasDependencyInfo&) const = default; - PYRO_NODISCARD PYRO_FORCEINLINE bool operator!=(const TaskBlasDependencyInfo&) const = default; - }; - struct TaskTlasDependencyInfo { - TaskTlas tlas; - TaskAccessType access; - - PYRO_NODISCARD PYRO_FORCEINLINE bool operator==(const TaskTlasDependencyInfo&) const = default; - PYRO_NODISCARD PYRO_FORCEINLINE bool operator!=(const TaskTlasDependencyInfo&) const = default; + PYRO_NODISCARD PYRO_FORCEINLINE bool operator==(const TaskAccelerationStructureDependencyInfo&) const = default; + PYRO_NODISCARD PYRO_FORCEINLINE bool operator!=(const TaskAccelerationStructureDependencyInfo&) const = default; }; struct TaskInfo { @@ -94,8 +87,7 @@ namespace PyroshockStudios { SHOCKGRAPH_API void UseBuffer(const TaskBufferDependencyInfo& info); SHOCKGRAPH_API void UseImage(const TaskImageDependencyInfo& info); - SHOCKGRAPH_API void UseBlas(const TaskBlasDependencyInfo& info); - SHOCKGRAPH_API void UseTlas(const TaskTlasDependencyInfo& info); + SHOCKGRAPH_API void UseAccelerationStructure(const TaskAccelerationStructureDependencyInfo& info); PYRO_NODISCARD PYRO_FORCEINLINE const TaskInfo& Info() const { return mTaskInfo; @@ -105,8 +97,7 @@ namespace PyroshockStudios { struct GenericSetup { eastl::vector bufferDepends; eastl::vector imageDepends; - eastl::vector blasDepends; - eastl::vector tlasDepends; + eastl::vector accelerationStructureDepends; }; GenericSetup mSetupData = {}; diff --git a/ShockGraph/TaskGraph.cpp b/ShockGraph/TaskGraph.cpp index 5786383..0bf9514 100644 --- a/ShockGraph/TaskGraph.cpp +++ b/ShockGraph/TaskGraph.cpp @@ -24,8 +24,11 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -362,22 +365,14 @@ namespace PyroshockStudios { commandBuffer->BlitImageToImage({ .srcImage = writeInfo.image->Internal(), .dstImage = swapImage, - .srcImageBox = { - .x = writeInfo.srcRect.x, - .y = writeInfo.srcRect.y, - .z = 0, - .width = writeInfo.srcRect.width, - .height = writeInfo.srcRect.height, - .depth = 1 - }, - .dstImageBox = { - .x = writeInfo.dstRect.x, - .y = writeInfo.dstRect.y, - .z = 0, - .width = writeInfo.dstRect.width, - .height = writeInfo.dstRect.height, - .depth = 1 - }, + .srcImageBox = { + .x = writeInfo.srcRect.x, + .y = writeInfo.srcRect.y, + .z = 0, + .width = writeInfo.srcRect.width, + .height = writeInfo.srcRect.height, + .depth = 1 }, + .dstImageBox = { .x = writeInfo.dstRect.x, .y = writeInfo.dstRect.y, .z = 0, .width = writeInfo.dstRect.width, .height = writeInfo.dstRect.height, .depth = 1 }, }); commandBuffer->ImageBarrier({ .image = swapImage, @@ -410,6 +405,8 @@ namespace PyroshockStudios { mTasks.clear(); mBatches.clear(); mAllTaskRefs.clear(); + mLastKnownBufferLayouts.clear(); + mLastKnownImageLayouts.clear(); bBaked = false; } SHOCKGRAPH_API void TaskGraph::Build() { @@ -449,6 +446,24 @@ namespace PyroshockStudios { } depedencyState.lastTaskId = eastl::make_optional(taskIndex); } + for (const auto& asDep : task->GetTask()->mSetupData.accelerationStructureDepends) { + u32 dependencyIndex = {}; + if (eastl::holds_alternative(asDep.accelerationStructure)) { + TaskBlas blas = eastl::get(asDep.accelerationStructure); + dependencyIndex = blas->GetId(); + } else if (eastl::holds_alternative(asDep.accelerationStructure)) { + TaskTlas tlas = eastl::get(asDep.accelerationStructure); + dependencyIndex = tlas->GetId(); + } else { + ASSERT(false, "Bad Acceleration Structure Variant!"); + } + + ResourceState& depedencyState = currentResources[dependencyIndex]; + if (depedencyState.lastTaskId.has_value()) { + currentTasks[taskIndex].parents.push_back(depedencyState.lastTaskId.value()); + } + depedencyState.lastTaskId = eastl::make_optional(taskIndex); + } } eastl::vector taskQueue = {}; @@ -504,7 +519,7 @@ namespace PyroshockStudios { barrier.srcAccess = dependencyState.currentAccess; barrier.dstLayout = AccessToBufferLayout(bufferDep.access); barrier.dstAccess = bufferDep.access; - batch.bufferBarriers.push_back(barrier); + batch.barriers.buffer.push_back(barrier); dependencyState.currentAccess = bufferDep.access; } } @@ -518,10 +533,34 @@ namespace PyroshockStudios { barrier.srcAccess = dependencyState.currentAccess; barrier.dstLayout = AccessToImageLayout(imageDep.access); barrier.dstAccess = imageDep.access; - batch.imageBarriers.push_back(barrier); + batch.barriers.image.push_back(barrier); dependencyState.currentAccess = imageDep.access; } } + for (const auto& asDepend : task->GetTask()->mSetupData.accelerationStructureDepends) { + eastl::variant asVariant = {}; + u32 dependencyIndex = {}; + if (eastl::holds_alternative(asDepend.accelerationStructure)) { + TaskBlas blas = eastl::get(asDepend.accelerationStructure); + asVariant = blas->Internal(); + dependencyIndex = blas->GetId(); + } else if (eastl::holds_alternative(asDepend.accelerationStructure)) { + TaskTlas tlas = eastl::get(asDepend.accelerationStructure); + asVariant = tlas->Internal(); + dependencyIndex = tlas->GetId(); + } else { + ASSERT(false, "Bad Acceleration Structure Variant!"); + } + ResourceState& dependencyState = currentResources[dependencyIndex]; + if (dependencyState.currentAccess != asDepend.access) { + AccelerationStructureBarrierInfo barrier{}; + barrier.accelerationStructure = asVariant; + barrier.srcAccess = dependencyState.currentAccess; + barrier.dstAccess = asDepend.access; + batch.barriers.accelerationStructure.push_back(barrier); + dependencyState.currentAccess = asDepend.access; + } + } } } @@ -565,6 +604,7 @@ namespace PyroshockStudios { previousTaskType = mTasks[batch.taskIds.back()]->GetTask()->GetType(); } } + Logger::Trace(mLogStream, "Injecting timestamp profilers"); for (i32 i = 0; i < mFramesInFlight; ++i) { mTimestampQueryPools.push_back(mDevice->CreateTimestampQueryPool({ @@ -652,20 +692,47 @@ namespace PyroshockStudios { for (Batch& batch : mBatches) { commandBuffer->BeginLabel({ .labelColor = LabelColor::BLACK, .name = "Sync Barriers Batch #" + eastl::to_string(batchIndex) }); - for (const auto& barrier : batch.imageBarriers) { + + for (auto barrier : batch.barriers.buffer) { + auto lastKnownLayout = mLastKnownBufferLayouts.find(barrier.buffer); + if (lastKnownLayout != mLastKnownBufferLayouts.end()) { + barrier.srcLayout = lastKnownLayout->second; + lastKnownLayout->second = barrier.dstLayout; + } else { + mLastKnownBufferLayouts[barrier.buffer] = barrier.dstLayout; + } + commandBuffer->BufferBarrier(barrier); + } + for (auto barrier : batch.barriers.image) { + auto lastKnownLayout = mLastKnownImageLayouts.find(barrier.image); + if (lastKnownLayout != mLastKnownImageLayouts.end()) { + barrier.srcLayout = lastKnownLayout->second; + lastKnownLayout->second = barrier.dstLayout; + } else { + mLastKnownImageLayouts[barrier.image] = barrier.dstLayout; + } commandBuffer->ImageBarrier(barrier); } - for (const auto& barrier : batch.bufferBarriers) { - commandBuffer->BufferBarrier(barrier); + + for (const auto& barrier : batch.barriers.accelerationStructure) { + commandBuffer->AccelerationStructureBarrier(barrier); } commandBuffer->EndLabel(); for (TaskId taskIndex : batch.taskIds) { TaskExecute* task = mTasks[taskIndex]; task->mTimestampPool = mTimestampQueryPools[mFrameIndex]; wrapper.mCurrBindPoint = task->GetTask()->GetBindPoint(); + + AccelerationStructureBarrierInfo barrier{}; + barrier.srcAccess = AccessConsts::READ_WRITE; + barrier.dstAccess = AccessConsts::READ_WRITE; + commandBuffer->AccelerationStructureBarrier(barrier); + task->PreExec(commandBuffer); task->GetTask()->ExecuteTask(wrapper); task->PostExec(commandBuffer); + + commandBuffer->AccelerationStructureBarrier(barrier); } ++batchIndex; } @@ -740,6 +807,7 @@ namespace PyroshockStudios { .srcLayout = BufferLayout::TransferDst, .dstLayout = stagingUpload.dstBufferLayout, }); + mLastKnownBufferLayouts[stagingUpload.dstBuffer] = stagingUpload.dstBufferLayout; } if (stagingUpload.dstImage) { commandBuffer->ImageBarrier({ @@ -762,6 +830,7 @@ namespace PyroshockStudios { .srcLayout = ImageLayout::TransferDst, .dstLayout = stagingUpload.dstImageLayout, }); + mLastKnownImageLayouts[stagingUpload.dstImage] = stagingUpload.dstImageLayout; } } mDevice->Destroy(uploadPair.srcBuffer, true); @@ -775,9 +844,11 @@ namespace PyroshockStudios { .name = "Flush dynamic buffers" }); for (const auto& bufferCopy : mResourceManager->mDynamicBuffers) { bufferCopy->mCurrentBufferInFlight = mFrameIndex; - if (bufferCopy->Info().bCpuVisible) { + if (bufferCopy->Info().mode == TaskBufferMode::HostDynamic || bufferCopy->Info().mode == TaskBufferMode::Readback) { + // This is purely stored on host, no copies needed bufferCopy->mBuffer = bufferCopy->InternalInFlightBuffer(mFrameIndex); - } else { + } else if (bufferCopy->Info().mode == TaskBufferMode::Dynamic) { + // Copy from host to device. commandBuffer->BufferBarrier({ .buffer = bufferCopy->InternalInFlightBuffer(mFrameIndex), .srcAccess = AccessConsts::HOST_WRITE, @@ -789,7 +860,7 @@ namespace PyroshockStudios { .buffer = bufferCopy->Internal(), .srcAccess = AccessConsts::NONE, .dstAccess = AccessConsts::TRANSFER_WRITE, - .srcLayout = BufferLayout::Undefined, + .srcLayout = BufferLayout::TransferDst, .dstLayout = BufferLayout::TransferDst, }); commandBuffer->CopyBufferToBuffer({ @@ -807,6 +878,7 @@ namespace PyroshockStudios { .srcLayout = BufferLayout::TransferDst, .dstLayout = BufferLayout::ReadOnly, }); + mLastKnownBufferLayouts[bufferCopy->Internal()] = BufferLayout::ReadOnly; } } commandBuffer->EndLabel(); @@ -816,52 +888,6 @@ namespace PyroshockStudios { eastl::string out; out += "TaskGraph Batches:\n"; - static const auto ImageLayoutToString = [](ImageLayout layout) -> eastl::string { - switch (layout) { - case ImageLayout::Identity: - return "Identity"; - case ImageLayout::Undefined: - return "Undefined"; - case ImageLayout::UnorderedAccess: - return "UnorderedAccess"; - case ImageLayout::ReadOnly: - return "ReadOnly"; - case ImageLayout::RenderTarget: - return "RenderTarget"; - case ImageLayout::TransferSrc: - return "TransferSrc"; - case ImageLayout::TransferDst: - return "TransferDst"; - case ImageLayout::BlitSrc: - return "BlitSrc"; - case ImageLayout::BlitDst: - return "BlitDst"; - case ImageLayout::PresentSrc: - return "PresentSrc"; - default: - return "Unknown"; - } - }; - - static const auto BufferLayoutToString = [](BufferLayout layout) -> eastl::string { - switch (layout) { - case BufferLayout::Identity: - return "Identity"; - case BufferLayout::Undefined: - return "Undefined"; - case BufferLayout::UnorderedAccess: - return "UnorderedAccess"; - case BufferLayout::ReadOnly: - return "ReadOnly"; - case BufferLayout::TransferSrc: - return "TransferSrc"; - case BufferLayout::TransferDst: - return "TransferDst"; - default: - return "Unknown"; - } - }; - static const auto ToHex = [](u64 value) -> eastl::string { char buf[32]; sprintf(buf, "0x%016llX", value); @@ -875,24 +901,41 @@ namespace PyroshockStudios { // Buffer Barriers out += " Buffer Barriers:\n"; - for (const auto& bb : batch.bufferBarriers) { + for (const auto& bb : batch.barriers.buffer) { auto name = mDevice->GetBufferInfo(bb.buffer).name; out += " Buffer: " + ToHex(eastl::bit_cast(bb.buffer)) + (name.empty() ? "" : (" {" + name + "}")) + ", Region: [off=" + eastl::to_string(bb.region.offset) + ", sz=" + ((bb.region.size == PYRO_MAX_SIZE) ? "WHOLE RANGE" : eastl::to_string(bb.region.size)) + "]" + - ", Layout: " + BufferLayoutToString(bb.srcLayout) + " -> " + - BufferLayoutToString(bb.dstLayout) + "\n"; + ", Layout: " + EnumToString(bb.srcLayout) + " -> " + + EnumToString(bb.dstLayout) + "\n"; } // Image Barriers out += " Image Barriers:\n"; - for (const auto& ib : batch.imageBarriers) { + for (const auto& ib : batch.barriers.image) { auto name = mDevice->GetImageInfo(ib.image).name; out += " Image: " + ToHex(eastl::bit_cast(ib.image)) + (name.empty() ? "" : (" {" + name + "}")) + - ", Slice: [mip=(" + eastl::to_string(ib.imageSlice.baseMipLevel) + ";" + eastl::to_string(ib.imageSlice.baseMipLevel + ib.imageSlice.levelCount - 1) + "), " + - "arr=(" + eastl::to_string(ib.imageSlice.baseArrayLayer) + ";" + eastl::to_string(ib.imageSlice.baseArrayLayer + ib.imageSlice.layerCount - 1) + ")]" + - ", Layout: " + ImageLayoutToString(ib.srcLayout) + " -> " + - ImageLayoutToString(ib.dstLayout) + "\n"; + ", Slice: [mip=(" + eastl::to_string(ib.imageSlice.baseMipLevel) + ";" + ((ib.imageSlice.levelCount == PYRO_REMAINING_MIP_LEVELS) ? "REMAINING MIPS" : (eastl::to_string(ib.imageSlice.baseMipLevel + ib.imageSlice.levelCount - 1))) + "), " + + "arr=(" + eastl::to_string(ib.imageSlice.baseArrayLayer) + ";" + ((ib.imageSlice.layerCount == PYRO_REMAINING_ARRAY_LAYERS) ? "REMAINING LAYERS" : (eastl::to_string(ib.imageSlice.baseArrayLayer + ib.imageSlice.layerCount - 1))) + ")]" + + ", Layout: " + EnumToString(ib.srcLayout) + " -> " + + EnumToString(ib.dstLayout) + "\n"; + } + + // Image Barriers + out += " Acceleration Structure Barriers:\n"; + for (const auto& asb : batch.barriers.accelerationStructure) { + out += " "; + if (eastl::holds_alternative(asb.accelerationStructure)) { + BlasId blas = eastl::get(asb.accelerationStructure); + auto name = mDevice->GetBlasInfo(blas).name; + out += "BLAS: " + ToHex(eastl::bit_cast(blas)) + (name.empty() ? "" : (" {" + name + "}")) + "\n"; + } else if (eastl::holds_alternative(asb.accelerationStructure)) { + TlasId tlas = eastl::get(asb.accelerationStructure); + auto name = mDevice->GetTlasInfo(tlas).name; + out += "TLAS: " + ToHex(eastl::bit_cast(tlas)) + (name.empty() ? "" : (" {" + name + "}")) + "\n"; + } else { + out += "!BAD VARIANT!\n"; + } } // Task IDs diff --git a/ShockGraph/TaskGraph.hpp b/ShockGraph/TaskGraph.hpp index 8d70c8d..7bad295 100644 --- a/ShockGraph/TaskGraph.hpp +++ b/ShockGraph/TaskGraph.hpp @@ -27,6 +27,7 @@ #include "TaskResourceManager.hpp" #include #include +#include #include #include @@ -93,16 +94,25 @@ namespace PyroshockStudios { IDevice* mDevice = {}; TaskResourceManager* mResourceManager = {}; + struct BatchBarrier { + eastl::vector buffer = {}; + eastl::vector image = {}; + eastl::vector accelerationStructure = {}; + }; struct Batch { eastl::vector taskIds = {}; - eastl::vector bufferBarriers = {}; - eastl::vector imageBarriers = {}; + BatchBarrier barriers = {}; }; ICommandQueue* mQueue = nullptr; eastl::vector> mInternalTasks = {}; eastl::vector mBatches = {}; + + // HACK: Pre-dx12 enhanced barriers, cannot assume COMMON->anything as a valid transition, so we must track. + eastl::hash_map mLastKnownBufferLayouts = {}; + eastl::hash_map mLastKnownImageLayouts = {}; + eastl::vector mTasks = {}; eastl::vector mSwapChains = {}; diff --git a/ShockGraph/TaskResourceManager.cpp b/ShockGraph/TaskResourceManager.cpp index 9660927..f2a3312 100644 --- a/ShockGraph/TaskResourceManager.cpp +++ b/ShockGraph/TaskResourceManager.cpp @@ -77,38 +77,47 @@ namespace PyroshockStudios { BufferUsageFlags extraRequiredFlags = {}; eastl::vector buffersInFlight{}; Buffer buffer = PYRO_NULL_BUFFER; - ASSERT(!(!info.bCpuVisible && info.bReadback), "Readback buffers MUST be "); if (!initialData.empty()) { extraRequiredFlags |= BufferUsageFlagBits::TRANSFER_DST; } - if (info.bDynamic) { + bool bExposeFlightBuffers = info.mode == TaskBufferMode::HostDynamic || info.mode == TaskBufferMode::Readback; + if (info.mode == TaskBufferMode::Dynamic || bExposeFlightBuffers) { buffersInFlight.resize(mFramesInFlight); for (i32 i = 0; i < mFramesInFlight; ++i) { buffersInFlight[i] = mDevice->CreateBuffer({ .size = info.size, - .usage = BufferUsageFlagBits::TRANSFER_SRC | extraRequiredFlags, - .initialLayout = info.bReadback ? BufferLayout::TransferDst : BufferLayout::TransferSrc, - .allocationDomain = info.bReadback ? MemoryAllocationDomain::HostReadback : MemoryAllocationDomain::HostRandomWrite, + .usage = (bExposeFlightBuffers ? (info.usage) : (BufferUsageFlagBits::TRANSFER_SRC)) | extraRequiredFlags, + .initialLayout = info.mode == TaskBufferMode::Readback ? BufferLayout::TransferDst : BufferLayout::TransferSrc, + .allocationDomain = info.mode == TaskBufferMode::Readback ? MemoryAllocationDomain::HostReadback : MemoryAllocationDomain::HostRandomWrite, .name = info.name + " (In Flight #" + eastl::to_string(i) + ")", }); } } - if (info.bDynamic && info.bCpuVisible) { + if (info.mode == TaskBufferMode::Readback || info.mode == TaskBufferMode::HostDynamic) { // No need to duplicate the buffers, can read write anyway buffer = buffersInFlight[0]; - } else { + } else if (info.mode == TaskBufferMode::Default) { buffer = mDevice->CreateBuffer({ .size = info.size, .usage = info.usage | extraRequiredFlags, - .initialLayout = info.bCpuVisible ? (info.bReadback ? BufferLayout::TransferDst : BufferLayout::ReadOnly) : BufferLayout::Undefined, - .allocationDomain = info.bCpuVisible ? (info.bReadback ? MemoryAllocationDomain::HostReadback : MemoryAllocationDomain::HostRandomWrite) : MemoryAllocationDomain::DeviceLocal, + .initialLayout = BufferLayout::Undefined, + .allocationDomain = MemoryAllocationDomain::DeviceLocal, .name = info.name, }); + } else if (info.mode == TaskBufferMode::Dynamic) { + buffer = mDevice->CreateBuffer({ + .size = info.size, + .usage = info.usage | extraRequiredFlags, + .initialLayout = BufferLayout::TransferDst, + .allocationDomain = MemoryAllocationDomain::DeviceLocal, + .name = info.name, + }); + } else { + ASSERT("Bad buffer mode!"); } if (!initialData.empty()) { - ASSERT(!info.bDynamic, "Cannot initialise a dynamic buffer with data!"); - ASSERT(!info.bReadback, "Cannot initialise a readback buffer with data!"); + ASSERT(info.mode == TaskBufferMode::Default, "Only buffers with Default mode can be initialised with data!"); ASSERT(initialData.size_bytes() >= info.size, "Initial data is too small in size!"); Buffer staging = mDevice->CreateBuffer({ .size = info.size, @@ -129,7 +138,7 @@ namespace PyroshockStudios { } TaskBuffer retBuffer = TaskBuffer::Create(this, info, eastl::move(buffer), eastl::move(buffersInFlight)); - if (info.bDynamic) { + if (info.mode == TaskBufferMode::Dynamic || info.mode == TaskBufferMode::HostDynamic || info.mode == TaskBufferMode::Readback) { mDynamicBuffers.emplace_back(retBuffer.Get()); } return retBuffer; @@ -198,7 +207,7 @@ namespace PyroshockStudios { .size = info.size, .name = info.name, }); - + return TaskBlas::Create(this, info, eastl::move(blas)); } @@ -207,7 +216,7 @@ namespace PyroshockStudios { .size = info.size, .name = info.name, }); - + return TaskTlas::Create(this, info, eastl::move(tlas)); } @@ -434,7 +443,8 @@ namespace PyroshockStudios { } void TaskResourceManager::ReleaseBufferResource(TaskBuffer_* resource) { - if (resource->Info().bDynamic) { + const auto& info = resource->Info(); + if (info.mode == TaskBufferMode::Dynamic || info.mode == TaskBufferMode::HostDynamic || info.mode == TaskBufferMode::Readback) { auto it = eastl::find(mDynamicBuffers.begin(), mDynamicBuffers.end(), resource); ASSERT(it != mDynamicBuffers.end()); mDynamicBuffers.erase(it); diff --git a/VisualTests/App.cpp b/VisualTests/App.cpp index 2be710e..cef4f9d 100644 --- a/VisualTests/App.cpp +++ b/VisualTests/App.cpp @@ -35,6 +35,7 @@ namespace VisualTests { StdoutLogger* gPlatformSink = nullptr; StdoutLogger* gRHILoaderSink = nullptr; StdoutLogger* gRHISink = nullptr; + StdoutLogger* gRHIValidationSink = nullptr; StdoutLogger* gSGSink = nullptr; ILogStream* gShaderSink = nullptr; @@ -49,6 +50,7 @@ namespace VisualTests { gPlatformSink = new StdoutLogger("PLATFORM"); gSGSink = new StdoutLogger("TASKGRAPH"); gRHILoaderSink = new StdoutLogger("RHILOADER"); + gRHIValidationSink = new StdoutLogger("RHIVALIDATION"); gShaderSink = new StdoutLogger("SLANGCOMPILER"); PlatformFactory::Get()->InjectLogger(gPlatformSink); @@ -72,6 +74,7 @@ namespace VisualTests { delete gPlatformSink; delete gSGSink; delete gRHILoaderSink; + delete gRHIValidationSink; delete static_cast(gShaderSink); if (gRHISink) { delete gRHISink; @@ -99,6 +102,8 @@ namespace VisualTests { mTaskRenderGraph->BeginFrame(); mTaskRenderGraph->Execute(); mTaskRenderGraph->EndFrame(); + + mRHIManager->GetRHIDevice()->CollectGarbage(); } } @@ -233,6 +238,7 @@ namespace VisualTests { delete gRHISink; gRHISink = new StdoutLogger(useRHIInfo.shorthand); createInfo.pLoggerSink = gRHISink; + createInfo.pDebugSink = gRHIValidationSink; if (!mRHIManager->AttachRHI(useRHI, createInfo)) { Logger::Warn(gRHILoaderSink, "Target RHI failed to attach, trying to attach next best RHI"); u32 numRhis = static_cast(mRHIManager->QueryAvailableRHIs().size()); diff --git a/VisualTests/ShaderCompiler.cpp b/VisualTests/ShaderCompiler.cpp index f9c5f97..92da59b 100644 --- a/VisualTests/ShaderCompiler.cpp +++ b/VisualTests/ShaderCompiler.cpp @@ -255,7 +255,7 @@ namespace VisualTests { std::string relativePathInInclude = std::filesystem::relative(file.path(), absoluteIncludeDir).string(); if (stream.eof()) { - Logger::Warn(gShaderSink, "Failed to load code from '" + eastl::string(relativePathInInclude.c_str()) + "'. Ignoring file..."); + Logger::Warn(gShaderSink, "Failed to load code from '{}'. Ignoring file...", relativePathInInclude); } slangRequest->addTranslationUnitSourceString(virtualFileIndex, relativePathInInclude.c_str(), stream.str().c_str()); @@ -268,9 +268,9 @@ namespace VisualTests { const char* diagnostics = slangRequest->getDiagnosticOutput(); if (diagnostics && strlen(diagnostics) > 0) { if (SLANG_FAILED(result)) { - Logger::Error(gShaderSink, "Slang failed to compile a shader! Diagnostics: " + eastl::string(diagnostics)); + Logger::Error(gShaderSink, "Slang failed to compile a shader! Diagnostics: {}", diagnostics); } else { - Logger::Warn(gShaderSink, "Slang compiled shader successfully, but generated diagnostics: " + eastl::string(diagnostics)); + Logger::Warn(gShaderSink, "Slang compiled shader successfully, but generated diagnostics: {}", diagnostics); } } ASSERT(result == 0); diff --git a/VisualTests/Tests/AlphaToCoverage.cpp b/VisualTests/Tests/AlphaToCoverage.cpp index c916b34..ef62838 100644 --- a/VisualTests/Tests/AlphaToCoverage.cpp +++ b/VisualTests/Tests/AlphaToCoverage.cpp @@ -29,7 +29,13 @@ namespace VisualTests { static const eastl::array gOffset1 = { 0.5, 0.0f }; void AlphaToCoverage::CreateResources(const CreateResourceInfo& info) { - RasterizationSamples sampleCount = info.resourceManager.GetInternalDevice()->Properties().msaaSupportColorTarget; + RasterizationSamples availableSampleCounts = info.resourceManager.GetInternalDevice()->Properties().msaaSupportColorTarget; + RasterizationSamples sampleCount = RasterizationSamples::e1; + while (availableSampleCounts != RasterizationSamples::e1) { + reinterpret_cast(availableSampleCounts) >>= 1; + reinterpret_cast(sampleCount) <<= 1; + } + image = info.resourceManager.CreatePersistentImage({ .format = Format::RGBA8Unorm, .size = { info.displayInfo.width, info.displayInfo.height }, diff --git a/VisualTests/Tests/ComputeUAV.cpp b/VisualTests/Tests/ComputeUAV.cpp index a59aa03..a1af5f6 100644 --- a/VisualTests/Tests/ComputeUAV.cpp +++ b/VisualTests/Tests/ComputeUAV.cpp @@ -52,12 +52,14 @@ namespace VisualTests { { .size = VERTEX_COUNT * sizeof(Vertex), .usage = BufferUsageFlagBits::VERTEX_BUFFER | BufferUsageFlagBits::UNORDERED_ACCESS, + .mode = TaskBufferMode::Default, .name = "Compute-UAV VBO/UAV", }); idxUav = info.resourceManager.CreatePersistentBuffer( { .size = INDEX_COUNT * sizeof(u32), .usage = BufferUsageFlagBits::INDEX_BUFFER | BufferUsageFlagBits::UNORDERED_ACCESS, + .mode = TaskBufferMode::Default, .name = "Compute-UAV Index buffer/UAV", }); vboUaView = info.resourceManager.CreateUnorderedAccessView({ .buffer = vboUav }); diff --git a/VisualTests/Tests/DrawIndirect.cpp b/VisualTests/Tests/DrawIndirect.cpp index 73f9544..7c906ab 100644 --- a/VisualTests/Tests/DrawIndirect.cpp +++ b/VisualTests/Tests/DrawIndirect.cpp @@ -76,6 +76,7 @@ namespace VisualTests { { .size = gVertices.size() * sizeof(Vertex), .usage = BufferUsageFlagBits::VERTEX_BUFFER, + .mode = TaskBufferMode::Default, .name = "Draw Indirect VBO", }, { reinterpret_cast(gVertices.data()), reinterpret_cast(gVertices.data() + gVertices.size()) }); @@ -84,6 +85,7 @@ namespace VisualTests { { .size = gDrawCmds.size() * sizeof(DrawArgumentBuffer), .usage = BufferUsageFlagBits::DRAW_INDIRECT, + .mode = TaskBufferMode::Default, .name = "Draw Indirect Argument Buffers", }, { reinterpret_cast(gDrawCmds.cbegin()), reinterpret_cast(gDrawCmds.cend()) }); diff --git a/VisualTests/Tests/IndexBuffer.cpp b/VisualTests/Tests/IndexBuffer.cpp index ff30b98..a140fed 100644 --- a/VisualTests/Tests/IndexBuffer.cpp +++ b/VisualTests/Tests/IndexBuffer.cpp @@ -57,6 +57,7 @@ namespace VisualTests { { .size = gVertices.size() * sizeof(Vertex), .usage = BufferUsageFlagBits::VERTEX_BUFFER, + .mode = TaskBufferMode::Default, .name = "Index Buffer VBO", }, { reinterpret_cast(gVertices.data()), reinterpret_cast(gVertices.data() + gVertices.size()) }); @@ -65,6 +66,7 @@ namespace VisualTests { { .size = gIndices.size() * sizeof(u32), .usage = BufferUsageFlagBits::INDEX_BUFFER, + .mode = TaskBufferMode::Default, .name = "Index Buffer Index Buffer", }, { reinterpret_cast(gIndices.data()), reinterpret_cast(gIndices.data() + gIndices.size()) }); diff --git a/VisualTests/Tests/InstanceBuffer.cpp b/VisualTests/Tests/InstanceBuffer.cpp index 8413cbd..f9beabb 100644 --- a/VisualTests/Tests/InstanceBuffer.cpp +++ b/VisualTests/Tests/InstanceBuffer.cpp @@ -60,6 +60,7 @@ namespace VisualTests { { .size = gVertices.size() * sizeof(Vertex), .usage = BufferUsageFlagBits::VERTEX_BUFFER, + .mode = TaskBufferMode::Default, .name = "Instance Buffer VBO", }, { reinterpret_cast(gVertices.data()), reinterpret_cast(gVertices.data() + gVertices.size()) }); @@ -68,6 +69,7 @@ namespace VisualTests { { .size = gInstances.size() * sizeof(InstanceData), .usage = BufferUsageFlagBits::VERTEX_BUFFER, + .mode = TaskBufferMode::Default, .name = "Instance Buffer IBO", }, { reinterpret_cast(gInstances.data()), reinterpret_cast(gInstances.data() + gInstances.size()) }); diff --git a/VisualTests/Tests/MSAA.cpp b/VisualTests/Tests/MSAA.cpp index c24ff71..2889b70 100644 --- a/VisualTests/Tests/MSAA.cpp +++ b/VisualTests/Tests/MSAA.cpp @@ -26,7 +26,13 @@ namespace VisualTests { void MSAA::CreateResources(const CreateResourceInfo& info) { - RasterizationSamples sampleCount = info.resourceManager.GetInternalDevice()->Properties().msaaSupportColorTarget; + RasterizationSamples availableSampleCounts = info.resourceManager.GetInternalDevice()->Properties().msaaSupportColorTarget; + RasterizationSamples sampleCount = RasterizationSamples::e1; + while (availableSampleCounts != RasterizationSamples::e1) { + reinterpret_cast(availableSampleCounts) >>= 1; + reinterpret_cast(sampleCount) <<= 1; + } + image = info.resourceManager.CreatePersistentImage({ .format = Format::RGBA8Unorm, .size = { info.displayInfo.width, info.displayInfo.height }, diff --git a/VisualTests/Tests/RayTracingCompute.cpp b/VisualTests/Tests/RayQueryCompute.cpp similarity index 66% rename from VisualTests/Tests/RayTracingCompute.cpp rename to VisualTests/Tests/RayQueryCompute.cpp index fb69ea6..78ce046 100644 --- a/VisualTests/Tests/RayTracingCompute.cpp +++ b/VisualTests/Tests/RayQueryCompute.cpp @@ -20,13 +20,16 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "RayTracingCompute.hpp" +#include "RayQueryCompute.hpp" namespace VisualTests { - - void RayTracingCompute::CreateResources(const CreateResourceInfo& info) { + // --- Build geometry buffers for BLAS/TLAS + struct SimpleVertex { + f32 x, y, z; + }; + void RayQueryCompute::CreateResources(const CreateResourceInfo& info) { image = info.resourceManager.CreatePersistentImage({ - .format = Format::RGBA8Unorm, + .format = Format::RGBA32Sfloat, .size = { info.displayInfo.width, info.displayInfo.height }, .usage = ImageUsageFlagBits::UNORDERED_ACCESS | ImageUsageFlagBits::TRANSFER_SRC | ImageUsageFlagBits::BLIT_SRC, .name = "Ray-Query Compute Image", @@ -34,37 +37,45 @@ namespace VisualTests { imageUav = info.resourceManager.CreateUnorderedAccessView({ .image = image }); - csh = info.shaderCompiler.CompileShaderFromFile("resources/VisualTests/Shaders/RayQuery.slang", + csh = info.shaderCompiler.CompileShaderFromFile("resources/VisualTests/Shaders/RayQueryCompute.slang", { .stage = ShaderStage::Compute, .entryPoint = "computeMain", .name = "RayQuery Compute" }); computePipeline = info.resourceManager.CreateComputePipeline({ .name = "RayQuery Compute Pipeline" }, { .program = csh }); - // --- Build geometry buffers for BLAS/TLAS - struct SimpleVertex { f32 x, y, z; }; - const SimpleVertex vertices[] = { { 3.f, 3.f, 4.f }, { -3.f, 3.f, 4.f }, { 0.f, -3.f, 4.f }, { 0.f, 0.f, 4.f } }; - const u32 indices[] = { 0, 1, 2, 1, 2, 3 }; + const SimpleVertex vertices[] = { + { -1.f, -1.f, 4.f }, + { 1.f, -1.f, 4.f }, + { 1.f, 1.f, 4.f }, + { -1.f, 1.f, 4.f } + }; + const u32 indices[] = { 0, 1, 2, 0, 2, 3 }; // Create vertex/index buffers with initial data - vertexBuffer = info.resourceManager.CreatePersistentBuffer({ - .size = sizeof(vertices), - .usage = BufferUsageFlagBits::BLAS_GEOMETRY_BUFFER, - .bCpuVisible = true, - .name = "RT Vertices", - }, eastl::span(reinterpret_cast(vertices), sizeof(vertices))); - - indexBuffer = info.resourceManager.CreatePersistentBuffer({ - .size = sizeof(indices), - .usage = BufferUsageFlagBits::BLAS_GEOMETRY_BUFFER, - .bCpuVisible = true, - .name = "RT Indices", - }, eastl::span(reinterpret_cast(indices), sizeof(indices))); + vertexBuffer = info.resourceManager.CreatePersistentBuffer( + { + .size = sizeof(vertices), + .usage = BufferUsageFlagBits::BLAS_GEOMETRY_BUFFER, + .mode = TaskBufferMode::Default, + .name = "RT Vertices", + }, + eastl::span(reinterpret_cast(vertices), sizeof(vertices))); + + indexBuffer = info.resourceManager.CreatePersistentBuffer( + { + .size = sizeof(indices), + .usage = BufferUsageFlagBits::BLAS_GEOMETRY_BUFFER, + .mode = TaskBufferMode::Default, + .name = "RT Indices", + }, + eastl::span(reinterpret_cast(indices), sizeof(indices))); // Get the internal device to query size requirements IDevice* device = info.resourceManager.GetInternalDevice(); // Prepare RHI Blas geometry info BlasTriangleGeometryInfo triGeo{}; + triGeo.flags = AccelerationStructureGeometryFlagBits::OPAQUE | AccelerationStructureGeometryFlagBits::NO_DUPLICATE_ANY_HIT_INVOCATION; triGeo.vertexFormat = Format::RGB32Sfloat; triGeo.indexType = IndexType::Uint32; triGeo.vertexBuffer = vertexBuffer->Internal(); @@ -94,15 +105,18 @@ namespace VisualTests { instanceData.instanceCustomIndex = 0; instanceData.mask = 0xFF; instanceData.instanceShaderBindingTableRecordOffset = 0; - instanceData.flags = 0; + instanceData.flags = AccelerationStructureGeometryInstanceFlagBits::FORCE_OPAQUE | + AccelerationStructureGeometryInstanceFlagBits::TRIANGLE_FACING_CULL_DISABLE; instanceData.blasAddress = device->BlasInstanceAddress(blas->Internal()); - instanceBuffer = info.resourceManager.CreatePersistentBuffer({ - .size = sizeof(BlasInstanceData), - .usage = BufferUsageFlagBits::BLAS_INSTANCE_BUFFER, - .bCpuVisible = true, - .name = "RT Instance Buffer", - }, eastl::span(reinterpret_cast(&instanceData), sizeof(instanceData))); + instanceBuffer = info.resourceManager.CreatePersistentBuffer( + { + .size = sizeof(BlasInstanceData), + .usage = BufferUsageFlagBits::BLAS_INSTANCE_BUFFER, + .mode = TaskBufferMode::Default, + .name = "RT Instance Buffer", + }, + eastl::span(reinterpret_cast(&instanceData), sizeof(instanceData))); // TLAS size requirements TlasInstanceInfo tlasInstanceInfo = { .data = instanceBuffer->Internal(), .count = 1 }; @@ -119,45 +133,61 @@ namespace VisualTests { } - void RayTracingCompute::ReleaseResources(const ReleaseResourceInfo& info) { + void RayQueryCompute::ReleaseResources(const ReleaseResourceInfo& info) { info.resourceManager.ReleaseUnorderedAccessView(imageUav); + computePipeline = {}; image = {}; csh = {}; - computePipeline = {}; + vertexBuffer = {}; + indexBuffer = {}; + instanceBuffer = {}; + blasScratchBuffer = {}; + tlasScratchBuffer = {}; + blas = {}; + tlas = {}; + bBuilt = false; } - eastl::span RayTracingCompute::CreateTasks() { - // Task 0: Build BLAS/TLAS + eastl::span RayQueryCompute::CreateTasks() { + // Task 1: Build BLAS/TLAS GenericTask* buildTask = new CustomCallbackTask( { .name = "Build Acceleration Structures", .color = LabelColor::YELLOW }, [this](CustomTask& task) { // declare buffer usage so task graph orders correctly - task.UseBuffer({ .buffer = vertexBuffer, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ_WRITE }); - task.UseBuffer({ .buffer = indexBuffer, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ_WRITE }); - task.UseBuffer({ .buffer = instanceBuffer, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ_WRITE }); + task.UseBuffer({ .buffer = vertexBuffer, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ }); + task.UseBuffer({ .buffer = indexBuffer, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ }); + task.UseBuffer({ .buffer = instanceBuffer, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ }); task.UseBuffer({ .buffer = blasScratchBuffer, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ_WRITE }); task.UseBuffer({ .buffer = tlasScratchBuffer, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ_WRITE }); - task.UseTlas({ .tlas = tlas, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ_WRITE }); - task.UseImage({ .image = image, .access = AccessConsts::BLIT_WRITE }); // this is retarded fix + task.UseAccelerationStructure({ .accelerationStructure = blas, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ_WRITE }); + task.UseAccelerationStructure({ .accelerationStructure = tlas, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ_WRITE }); }, [this](ICommandBuffer* commands) { - // Prepare RHI build infos + if (bBuilt) + return; + bBuilt = true; + // Prepare RHI build infos BlasTriangleGeometryInfo triGeo{}; + triGeo.flags = AccelerationStructureGeometryFlagBits::OPAQUE | AccelerationStructureGeometryFlagBits::NO_DUPLICATE_ANY_HIT_INVOCATION; triGeo.vertexFormat = Format::RGB32Sfloat; triGeo.indexType = IndexType::Uint32; triGeo.vertexBuffer = vertexBuffer->Internal(); triGeo.indexBuffer = indexBuffer->Internal(); - triGeo.vertexStride = sizeof(float) * 3; - triGeo.vertexCount = 3; - triGeo.indexCount = 3; + triGeo.vertexStride = sizeof(SimpleVertex); + triGeo.vertexCount = 4; + triGeo.indexCount = 6; eastl::array geoArray = { triGeo }; - BlasBuildInfo blasBuildInfo = { .geometries = geoArray }; + BlasBuildInfo blasBuildInfo{}; + blasBuildInfo.geometries = geoArray; blasBuildInfo.dstBlas = blas->Internal(); blasBuildInfo.scratchBuffer = blasScratchBuffer->Internal(); TlasInstanceInfo tlasInstanceInfo = { .data = instanceBuffer->Internal(), .count = 1 }; - TlasBuildInfo tlasBuildInfo = { .instances = tlasInstanceInfo }; + tlasInstanceInfo.flags = AccelerationStructureGeometryFlagBits::OPAQUE | AccelerationStructureGeometryFlagBits::NO_DUPLICATE_ANY_HIT_INVOCATION; + + TlasBuildInfo tlasBuildInfo{}; + tlasBuildInfo.instances = tlasInstanceInfo; tlasBuildInfo.dstTlas = tlas->Internal(); tlasBuildInfo.scratchBuffer = tlasScratchBuffer->Internal(); @@ -170,12 +200,13 @@ namespace VisualTests { }, TaskType::Transfer); - // Task 1: Dispatch compute shader which performs ray queries + // Task 2: Dispatch compute shader which performs ray queries GenericTask* dispatchTask = new ComputeCallbackTask( { .name = "RayQuery Compute Dispatch", .color = LabelColor::YELLOW }, [this](ComputeTask& task) { task.UseImage({ .image = image, .access = AccessConsts::COMPUTE_SHADER_WRITE }); - task.UseTlas({ .tlas = tlas, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ }); + task.UseAccelerationStructure({ .accelerationStructure = blas, .access = AccessConsts::COMPUTE_SHADER_READ }); + task.UseAccelerationStructure({ .accelerationStructure = tlas, .access = AccessConsts::COMPUTE_SHADER_READ }); }, [this](TaskCommandList& commands) { commands.SetComputePipeline(computePipeline); @@ -190,32 +221,7 @@ namespace VisualTests { commands.Dispatch({ .x = gx, .y = gy }); }); - // Task 2: Blit compute image to a full-size blit image that will be presented - // GenericTask* blitTask = new CustomCallbackTask( - // { .name = "Blit To Swapchain Image", .color = LabelColor::YELLOW }, - // [this](CustomTask& task) { - // task.UseImage({ .image = image, - // .access = AccessConsts::BLIT_READ }); - // task.UseImage({ .image = blitImage, - // .access = AccessConsts::BLIT_WRITE }); - // }, - // [this](ICommandBuffer* commands) { - // BlitImageToImageInfo blitInfo{}; - // blitInfo.srcImage = image->Internal(); - // blitInfo.dstImage = blitImage->Internal(); - - // Extent3D srcDim = image->Info().size; - // Extent3D dstDim = blitImage->Info().size; - - // blitInfo.srcImageBox = Box3D::Cut({ srcDim.width, srcDim.height, 1 }); - // blitInfo.dstImageBox = Box3D::Cut({ dstDim.width, dstDim.height, 1 }); - // blitInfo.filter = Filter::Nearest; - - // commands->BlitImageToImage(blitInfo); - // }, - // TaskType::Graphics); - - tasks = { buildTask, dispatchTask }; + tasks = { buildTask, dispatchTask }; return tasks; } diff --git a/VisualTests/Tests/RayTracingCompute.hpp b/VisualTests/Tests/RayQueryCompute.hpp similarity index 87% rename from VisualTests/Tests/RayTracingCompute.hpp rename to VisualTests/Tests/RayQueryCompute.hpp index f26d3f3..ad95bdc 100644 --- a/VisualTests/Tests/RayTracingCompute.hpp +++ b/VisualTests/Tests/RayQueryCompute.hpp @@ -25,7 +25,7 @@ namespace VisualTests { - class RayTracingCompute : public IVisualTest, DeleteCopy, DeleteMove { + class RayQueryCompute : public IVisualTest, DeleteCopy, DeleteMove { eastl::string Title() const override { return "Ray Query Compute"; } void CreateResources(const CreateResourceInfo& info) override; @@ -33,11 +33,11 @@ namespace VisualTests { eastl::span CreateTasks() override; bool UseTaskGraph() const override { return true; } - TaskImage GetCompositeImageTaskGraph() override { return image; } - Image GetCompositeImageRaw() override { return image->Internal(); } + TaskImage GetCompositeImageTaskGraph() override { return image; } + Image GetCompositeImageRaw() override { return image->Internal(); } private: - TaskImage image; + TaskImage image; UnorderedAccessId imageUav; TaskShader csh; TaskComputePipeline computePipeline; @@ -50,7 +50,7 @@ namespace VisualTests { TaskBuffer tlasScratchBuffer; TaskBlas blas; TaskTlas tlas; - + bool bBuilt = false; eastl::vector tasks = {}; }; } // namespace VisualTests diff --git a/VisualTests/Tests/RayQueryPixel.cpp b/VisualTests/Tests/RayQueryPixel.cpp new file mode 100644 index 0000000..ab313fc --- /dev/null +++ b/VisualTests/Tests/RayQueryPixel.cpp @@ -0,0 +1,213 @@ +// MIT License +// +// Copyright (c) 2025 Pyroshock Studios +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "RayQueryPixel.hpp" + +namespace VisualTests { + // --- Build geometry buffers for BLAS/TLAS + struct SimpleVertex { + f32 x, y, z; + }; + void RayQueryPixel::CreateResources(const CreateResourceInfo& info) { + image = info.resourceManager.CreatePersistentImage({ + .format = Format::RGBA8Unorm, + .size = { info.displayInfo.width, info.displayInfo.height }, + .usage = ImageUsageFlagBits::RENDER_TARGET | ImageUsageFlagBits::TRANSFER_SRC | ImageUsageFlagBits::BLIT_SRC, + .name = "Hello Texture Render Image", + }); + + imageTarget = info.resourceManager.CreateColorTarget({ + .image = image, + .name = "Hello Texture RT", + }); + + vsh = info.shaderCompiler.CompileShaderFromFile("resources/VisualTests/Shaders/RayQuery.slang", + { .stage = ShaderStage::Vertex, .entryPoint = "vertexMain", .name = "RayQuery Vsh" }); + fsh = info.shaderCompiler.CompileShaderFromFile("resources/VisualTests/Shaders/RayQuery.slang", + { .stage = ShaderStage::Fragment, .entryPoint = "fragmentMain", .name = "RayQuery Fsh" }); + pipeline = info.resourceManager.CreateRasterPipeline( + { + .colorTargetStates = { { .format = image->Info().format } }, + .name = "Raster Pipeline", + }, + { + .vertexShaderInfo = { TaskShaderInfo{ .program = vsh } }, + .fragmentShaderInfo = { TaskShaderInfo{ .program = fsh } }, + }); + + // Create vertex/index buffers with initial data + vertexBuffer = info.resourceManager.CreatePersistentBuffer( + { + .size = sizeof(SimpleVertex) * 4, + .usage = BufferUsageFlagBits::BLAS_GEOMETRY_BUFFER, + .mode = TaskBufferMode::HostDynamic, + .name = "RT Vertices", + }); + + indexBuffer = info.resourceManager.CreatePersistentBuffer( + { + .size = sizeof(u32) * 6, + .usage = BufferUsageFlagBits::BLAS_GEOMETRY_BUFFER, + .mode = TaskBufferMode::HostDynamic, + .name = "RT Indices", + }); + + // Get the internal device to query size requirements + IDevice* device = info.resourceManager.GetInternalDevice(); + + // Prepare RHI Blas geometry info + BlasTriangleGeometryInfo triGeo{}; + triGeo.flags = AccelerationStructureGeometryFlagBits::OPAQUE; + triGeo.vertexFormat = Format::RGB32Sfloat; + triGeo.indexType = IndexType::Uint32; + triGeo.vertexBuffer = vertexBuffer->Internal(); + triGeo.indexBuffer = indexBuffer->Internal(); + triGeo.vertexStride = sizeof(SimpleVertex); + triGeo.vertexCount = 4; + triGeo.indexCount = 6; + + eastl::array geoArray = { triGeo }; + BlasBuildInfo blasBuildInfo = { .geometries = geoArray }; + + AccelerationStructureBuildSizesInfo blasSizeInfo = device->BlasSizeRequirements(blasBuildInfo); + + // Create BLAS resource + blas = info.resourceManager.CreatePersistentBlas({ .size = blasSizeInfo.accelerationStructureSize, .name = "RT Blas" }); + + // Create scratch buffers for BLAS and TLAS + blasScratchBuffer = info.resourceManager.CreatePersistentBuffer({ + .size = blasSizeInfo.buildScratchSize, + .usage = BufferUsageFlagBits::ACCELERATION_STRUCTURE_SCRATCH_BUFFER, + .name = "RT Blas Scratch", + }); + + + instanceBuffer = info.resourceManager.CreatePersistentBuffer( + { + .size = sizeof(BlasInstanceData), + .usage = BufferUsageFlagBits::BLAS_INSTANCE_BUFFER, + .mode = TaskBufferMode::HostDynamic, + .name = "RT Instance Buffer", + }); + + // TLAS size requirements + TlasInstanceInfo tlasInstanceInfo = { .data = instanceBuffer->Internal(), .count = 1 }; + tlasInstanceInfo.flags = AccelerationStructureGeometryFlagBits::OPAQUE; + TlasBuildInfo tlasBuildInfo = { .instances = tlasInstanceInfo }; + AccelerationStructureBuildSizesInfo tlasSizeInfo = device->TlasSizeRequirements(tlasBuildInfo); + + // Create TLAS and scratch + tlas = info.resourceManager.CreatePersistentTlas({ .size = tlasSizeInfo.accelerationStructureSize, .name = "RT Tlas" }); + tlasScratchBuffer = info.resourceManager.CreatePersistentBuffer({ + .size = tlasSizeInfo.buildScratchSize, + .usage = BufferUsageFlagBits::ACCELERATION_STRUCTURE_SCRATCH_BUFFER, + .name = "RT Tlas Scratch", + }); + + + // build AS + + const SimpleVertex vertices[] = { + { -1.f, -1.f, 4.f }, + { 1.f, -1.f, 4.f }, + { 1.f, 1.f, 4.f }, + { -1.f, 1.f, 4.f } + }; + memcpy(vertexBuffer->MappedMemory(), vertices, sizeof(vertices)); + const u32 indices[] = { 0, 1, 2, 0, 2, 3 }; + memcpy(indexBuffer->MappedMemory(), indices, sizeof(indices)); + + // Instance buffer for TLAS + BlasInstanceData& instanceData = *reinterpret_cast(instanceBuffer->MappedMemory()); + instanceData.transform = Transform::IDENTITY; + instanceData.instanceCustomIndex = 0; + instanceData.mask = 0xFF; + instanceData.instanceShaderBindingTableRecordOffset = 0; + instanceData.flags = AccelerationStructureGeometryInstanceFlagBits::TRIANGLE_FACING_CULL_DISABLE; + instanceData.blasAddress = blas->InstanceAddress(); + + + blasBuildInfo.geometries = geoArray; + blasBuildInfo.dstBlas = blas->Internal(); + blasBuildInfo.scratchBuffer = blasScratchBuffer->Internal(); + + tlasBuildInfo.instances = tlasInstanceInfo; + tlasBuildInfo.dstTlas = tlas->Internal(); + tlasBuildInfo.scratchBuffer = tlasScratchBuffer->Internal(); + + BuildAccelerationStructuresInfo buildAllInfo = { + .tlasBuildInfos = eastl::span(&tlasBuildInfo, 1), + .blasBuildInfos = eastl::span(&blasBuildInfo, 1), + }; + + ICommandBuffer* singleTimeCommands = device->GetPresentQueue()->GetCommandBuffer({ .name = "Single time build commands" }); + singleTimeCommands->BuildAccelerationStructures(buildAllInfo); + singleTimeCommands->Complete(); + device->GetPresentQueue()->SubmitCommandBuffer(singleTimeCommands); + device->SubmitQueue({ .queue = device->GetPresentQueue() }); + device->WaitIdle(); + } + + + void RayQueryPixel::ReleaseResources(const ReleaseResourceInfo& info) { + pipeline = {}; + imageTarget = {}; + image = {}; + fsh = {}; + vsh = {}; + vertexBuffer = {}; + indexBuffer = {}; + instanceBuffer = {}; + blasScratchBuffer = {}; + tlasScratchBuffer = {}; + blas = {}; + tlas = {}; + bBuilt = false; + } + + eastl::span RayQueryPixel::CreateTasks() { + + // Task 2: Dispatch compute shader which performs ray queries + GenericTask* drawTask = new GraphicsCallbackTask( + { .name = "RayQuery Draw Call", .color = LabelColor::YELLOW }, + [this](GraphicsTask& task) { + task.BindColorTarget({ + .target = imageTarget, + .clear = eastl::make_optional(eastl::array{ 0, 0, 0, 0 }), + }); + task.UseAccelerationStructure({ .accelerationStructure = blas, .access = AccessConsts::FRAGMENT_SHADER_READ }); + task.UseAccelerationStructure({ .accelerationStructure = tlas, .access = AccessConsts::FRAGMENT_SHADER_READ }); + }, + [this](TaskCommandList& commands) { + commands.SetRasterPipeline(pipeline); + // push TLAS index so shader can index the descriptor array + u32 tlasIndex = tlas->Internal().index; + commands.PushConstant(tlasIndex); + commands.Draw({ .vertexCount = 6 }); + }); + + tasks = { drawTask }; + + return tasks; + } + +} // namespace VisualTests diff --git a/VisualTests/Tests/RayQueryPixel.hpp b/VisualTests/Tests/RayQueryPixel.hpp new file mode 100644 index 0000000..37eb2a1 --- /dev/null +++ b/VisualTests/Tests/RayQueryPixel.hpp @@ -0,0 +1,57 @@ +// MIT License +// +// Copyright (c) 2025 Pyroshock Studios +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once +#include + + +namespace VisualTests { + class RayQueryPixel : public IVisualTest, DeleteCopy, DeleteMove { + eastl::string Title() const override { return "Ray Query Pixel Shader"; } + + void CreateResources(const CreateResourceInfo& info) override; + void ReleaseResources(const ReleaseResourceInfo& info) override; + eastl::span CreateTasks() override; + + bool UseTaskGraph() const override { return true; } + TaskImage GetCompositeImageTaskGraph() override { return image; } + Image GetCompositeImageRaw() override { return image->Internal(); } + + private: + TaskImage image; + TaskColorTarget imageTarget; + TaskShader vsh; + TaskShader fsh; + TaskRasterPipeline pipeline; + + // Acceleration structure resources + TaskBuffer vertexBuffer; + TaskBuffer indexBuffer; + TaskBuffer instanceBuffer; + TaskBuffer blasScratchBuffer; + TaskBuffer tlasScratchBuffer; + TaskBlas blas; + TaskTlas tlas; + bool bBuilt = false; + eastl::vector tasks = {}; + }; +} // namespace VisualTests diff --git a/VisualTests/Tests/UniformBuffer.cpp b/VisualTests/Tests/UniformBuffer.cpp index d518625..c7b1ff1 100644 --- a/VisualTests/Tests/UniformBuffer.cpp +++ b/VisualTests/Tests/UniformBuffer.cpp @@ -90,7 +90,7 @@ namespace VisualTests { ubo = info.resourceManager.CreatePersistentBuffer({ .size = sizeof(GlobalUbo), .usage = BufferUsageFlagBits::UNIFORM_BUFFER, - .bDynamic = true, + .mode = TaskBufferMode::Dynamic, .name = "Vertex Colours Uniform Buffer", }); target = info.resourceManager.CreateColorTarget({ diff --git a/VisualTests/Tests/UpdateBuffer.cpp b/VisualTests/Tests/UpdateBuffer.cpp index 4d1d94f..566bd15 100644 --- a/VisualTests/Tests/UpdateBuffer.cpp +++ b/VisualTests/Tests/UpdateBuffer.cpp @@ -34,8 +34,7 @@ namespace VisualTests { ubo = info.resourceManager.CreatePersistentBuffer({ .size = sizeof(f32), .usage = BufferUsageFlagBits::UNIFORM_BUFFER | BufferUsageFlagBits::TRANSFER_DST /*Transfer DST is required*/, - .bCpuVisible = false, // Not cpu visible! Should not be needed for UpdateBuffer()! - .bDynamic = false, // Not dynamic! Should not be needed either! + .mode = TaskBufferMode::Default, // Not cpu visible nor dynamic! Should not be needed for UpdateBuffer()! .name = "Vertex Scale Uniform Buffer", }); target = info.resourceManager.CreateColorTarget({ diff --git a/VisualTests/Tests/VertexBuffer.cpp b/VisualTests/Tests/VertexBuffer.cpp index 229f6ff..1523aec 100644 --- a/VisualTests/Tests/VertexBuffer.cpp +++ b/VisualTests/Tests/VertexBuffer.cpp @@ -52,6 +52,7 @@ namespace VisualTests { { .size = gVertices.size() * sizeof(Vertex), .usage = BufferUsageFlagBits::VERTEX_BUFFER, + .mode = TaskBufferMode::Default, .name = "Vertex Buffer VBO", }, { (u8*)gVertices.cbegin(), (u8*)gVertices.cend() }); diff --git a/VisualTests/main.cpp b/VisualTests/main.cpp index e4066c8..8f72c18 100644 --- a/VisualTests/main.cpp +++ b/VisualTests/main.cpp @@ -32,7 +32,8 @@ #include "Tests/AlphaToCoverage.hpp" #include "Tests/BlitImage.hpp" #include "Tests/ComputeUAV.hpp" -#include "Tests/RayTracingCompute.hpp" +#include "Tests/RayQueryCompute.hpp" +#include "Tests/RayQueryPixel.hpp" #include "Tests/DrawIndirect.hpp" #include "Tests/GeometryShader.hpp" #include "Tests/HelloTexture.hpp" @@ -68,6 +69,8 @@ using namespace VisualTests; int main(i32 argc, char** argv) { VisualTestApp* app = new VisualTestApp(); + app->RegisterTest(); + app->RegisterTest(); app->RegisterTest(); app->RegisterTest(); app->RegisterTest(); @@ -85,7 +88,6 @@ int main(i32 argc, char** argv) { app->RegisterTest(); app->RegisterTest(); app->RegisterTest(); - app->RegisterTest(); app->RegisterTest(); app->RegisterTest(); diff --git a/resources/Shaders/Include/Common/DescriptorIndexing.slang b/resources/Shaders/Include/Common/DescriptorIndexing.slang index 5b4a5e6..339be30 100644 --- a/resources/Shaders/Include/Common/DescriptorIndexing.slang +++ b/resources/Shaders/Include/Common/DescriptorIndexing.slang @@ -6,22 +6,24 @@ typedef uint32_t PyroDescriptor; #define pyro_internal_access(index, resource) resource[index] #define PYRO_ACCESS(index, resource) pyro_internal_access(index, resource) // Byte address buffers -[[vk::binding(0)]] ByteAddressBuffer gByteBuffer[] : register(t0, space1); +[[vk::binding(0)]] ByteAddressBuffer gByteBuffer[] : register(t0, space1); // Textures (SRVs) -[[vk::binding(1)]] Texture1D gTexture1D[] : register(t0, space1); -[[vk::binding(1)]] Texture2D gTexture2D[] : register(t0, space1); -[[vk::binding(1)]] TextureCube gTextureCube[] : register(t0, space1); -[[vk::binding(1)]] Texture3D gTexture3D[] : register(t0, space1); -[[vk::binding(1)]] Texture1DArray gTexture1DArray[] : register(t0, space1); -[[vk::binding(1)]] Texture2DArray gTexture2DArray[] : register(t0, space1); -[[vk::binding(1)]] TextureCubeArray gTextureCubeArray[] : register(t0, space1); -[[vk::binding(1)]] Texture2DMS gTexture2DMS[] : register(t0, space1); -[[vk::binding(1)]] Texture2DMSArray gTexture2DMSArray[] : register(t0, space1); +[[vk::binding(1)]] Texture1D gTexture1D[] : register(t0, space1); +[[vk::binding(1)]] Texture2D gTexture2D[] : register(t0, space1); +[[vk::binding(1)]] TextureCube gTextureCube[] : register(t0, space1); +[[vk::binding(1)]] Texture3D gTexture3D[] : register(t0, space1); +[[vk::binding(1)]] Texture1DArray gTexture1DArray[] : register(t0, space1); +[[vk::binding(1)]] Texture2DArray gTexture2DArray[] : register(t0, space1); +[[vk::binding(1)]] TextureCubeArray gTextureCubeArray[] : register(t0, space1); +[[vk::binding(1)]] Texture2DMS gTexture2DMS[] : register(t0, space1); +[[vk::binding(1)]] Texture2DMSArray gTexture2DMSArray[] : register(t0, space1); // Samplers -[[vk::binding(2)]] SamplerState gSampler[] : register(s0, space1); -[[vk::binding(2)]] SamplerComparisonState gSamplerComparison[] : register(s0, space1); - +[[vk::binding(2)]] SamplerState gSampler[] : register(s0, space1); +[[vk::binding(2)]] SamplerComparisonState gSamplerComparison[] : register(s0, space1); +// Acceleration Structures +[[vk::binding(3)]] RaytracingAccelerationStructure gTlas[] : register(t0, space1); + #endif diff --git a/resources/VisualTests/Shaders/RayQuery.slang b/resources/VisualTests/Shaders/RayQuery.slang index 87e7e51..54e3e99 100644 --- a/resources/VisualTests/Shaders/RayQuery.slang +++ b/resources/VisualTests/Shaders/RayQuery.slang @@ -1,38 +1,76 @@ -#include #include +#include +#include -// Bind an array of TLAS handles at t0 (space1). The runtime maps created TLAS into this array. -[[vk::binding(3, 0)]] RaytracingAccelerationStructure gTlas[] : register(t0, space1); -PYRO_BIND_UNORDERED_ACCESS_TEXTURE_2D(0, float4, gOutput); +PYRO_PUSH_CONSTANT(PyroDescriptor, tlasIndex); -[[push_constant]] cbuffer pyro_PushConstant : register(b13, space0) { uint tlasIndex; }; +struct VertexOutput { + float4 position : SV_Position; + float2 uv : TEXCOORD0; +}; -[numthreads(8,8,1)] -void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) { - RayQuery query; +VertexOutput vertexMain(uint vertexID : SV_VertexID) +{ + // 6 vertices for 2 triangles + float2 positions[6] = { + float2(-1.0f, 1.0f), // Top-left + float2(-1.0f, -1.0f), // Bottom-left + float2( 1.0f, -1.0f), // Bottom-right - int width, height; - gOutput.GetDimensions(width, height); + float2(-1.0f, 1.0f), // Top-left + float2( 1.0f, -1.0f), // Bottom-right + float2( 1.0f, 1.0f) // Top-right + }; - RayDesc ray; - float2 uv = (dispatchThreadID.xy + 0.5) / float2(width, height); - float3 origin = float3(uv * 2.0 - 1.0, -1.0); + float2 uvs[6] = { + float2(0.0f, 0.0f), + float2(0.0f, 1.0f), + float2(1.0f, 1.0f), + + float2(0.0f, 0.0f), + float2(1.0f, 1.0f), + float2(1.0f, 0.0f) + }; + + VertexOutput output; + output.position = float4(positions[vertexID], 0.0, 1.0); + output.uv = uvs[vertexID]; + return output; +} + +float4 fragmentMain(VertexOutput input) : SV_Target +{ + + const float ZOOM = 0.2; + + float2 ndc = input.uv * 2.0 - 1.0; + float3 origin = float3(ndc / ZOOM, 0.0); float3 direction = float3(0.0, 0.0, 1.0); + RayDesc ray; ray.Origin = origin; ray.Direction = direction; - ray.TMin = 0.001; - ray.TMax = 1000.0; + ray.TMin = 0.1; + ray.TMax = 100.0; - query.TraceRayInline( + RayQuery query; + query.TraceRayInline( gTlas[tlasIndex], - RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH, + RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_FORCE_OPAQUE, 0xFF, ray ); uint hit = 0; - if (query.CommittedStatus() == COMMITTED_TRIANGLE_HIT) { - hit = 1; - } - gOutput[dispatchThreadID.xy] = hit == 1 ? float4(1.0, 0.0, 0.0, 1.0) : float4(0.0, 0.0, 0.0, 1.0); + query.Proceed(); + + if (query.CommittedStatus() == COMMITTED_TRIANGLE_HIT) + { + hit = 1; + } + else + { + hit = 0; // No committed hit found + } + + return hit == 1 ? float4(1.0, 0.0, 0.0, 1.0) : float4(0.0, 0.0, 0.0, 1.0); // float4(origin, 1.0); } diff --git a/resources/VisualTests/Shaders/RayQueryCompute.slang b/resources/VisualTests/Shaders/RayQueryCompute.slang new file mode 100644 index 0000000..eb5ff56 --- /dev/null +++ b/resources/VisualTests/Shaders/RayQueryCompute.slang @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +PYRO_BIND_UNORDERED_ACCESS_TEXTURE_2D(0, float4, gOutput); +PYRO_PUSH_CONSTANT(PyroDescriptor, tlasIndex); + +[numthreads(8,8,1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) { + + const float ZOOM = 0.2; + int width, height; + gOutput.GetDimensions(width, height); + + RayDesc ray; + float2 uv = (dispatchThreadID.xy + 0.5) / float2(width, height); + float2 ndc = uv * 2.0 - 1.0; + float3 origin = float3(ndc / ZOOM, 0.0); + float3 direction = float3(0.0, 0.0, 1.0); + ray.Origin = origin; + ray.Direction = direction; + ray.TMin = 0.1; + ray.TMax = 100.0; + + RayQuery query; + query.TraceRayInline( + gTlas[tlasIndex], + RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_FORCE_OPAQUE, + 0xFF, + ray + ); + + uint hit = 0; + query.Proceed(); + + if (query.CommittedStatus() == COMMITTED_TRIANGLE_HIT) + { + hit = 1; + } + else + { + hit = 0; // No committed hit found + } + + gOutput[dispatchThreadID.xy] = hit == 1 ? float4(1.0, 0.0, 0.0, 1.0) : float4(0.0, 0.0, 0.0, 1.0); // float4(origin, 1.0); +} diff --git a/vendor/PyroRHI b/vendor/PyroRHI index f3f0f93..9a25fc5 160000 --- a/vendor/PyroRHI +++ b/vendor/PyroRHI @@ -1 +1 @@ -Subproject commit f3f0f93cff2263eb701c51c669645675019da53f +Subproject commit 9a25fc5346b4b47aaf02739e735d561c681fc2f9 From fa878afa0b31bca1bde1758bdb3bb7ec99da8a0b Mon Sep 17 00:00:00 2001 From: ZilverBlade <90721817+ZilverBlade@users.noreply.github.com> Date: Sat, 15 Nov 2025 02:19:03 +0100 Subject: [PATCH 3/4] the stupid stuff is still not working i hate ray tracing it was the worst invention ever we shouldve just stuck with good old rasterisation because why tf do you need to do real time ray tracing like that's not even close to making sense --- VisualTests/Tests/RayQueryPixel.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/VisualTests/Tests/RayQueryPixel.cpp b/VisualTests/Tests/RayQueryPixel.cpp index ab313fc..81127e7 100644 --- a/VisualTests/Tests/RayQueryPixel.cpp +++ b/VisualTests/Tests/RayQueryPixel.cpp @@ -76,7 +76,8 @@ namespace VisualTests { // Prepare RHI Blas geometry info BlasTriangleGeometryInfo triGeo{}; - triGeo.flags = AccelerationStructureGeometryFlagBits::OPAQUE; + triGeo.flags = AccelerationStructureGeometryFlagBits::OPAQUE | + AccelerationStructureGeometryFlagBits::NO_DUPLICATE_ANY_HIT_INVOCATION; triGeo.vertexFormat = Format::RGB32Sfloat; triGeo.indexType = IndexType::Uint32; triGeo.vertexBuffer = vertexBuffer->Internal(); @@ -84,10 +85,10 @@ namespace VisualTests { triGeo.vertexStride = sizeof(SimpleVertex); triGeo.vertexCount = 4; triGeo.indexCount = 6; - + eastl::array geoArray = { triGeo }; BlasBuildInfo blasBuildInfo = { .geometries = geoArray }; - + blasBuildInfo.flags = AccelerationStructureCreateFlagBits::PREFER_FAST_TRACE; AccelerationStructureBuildSizesInfo blasSizeInfo = device->BlasSizeRequirements(blasBuildInfo); // Create BLAS resource @@ -111,8 +112,10 @@ namespace VisualTests { // TLAS size requirements TlasInstanceInfo tlasInstanceInfo = { .data = instanceBuffer->Internal(), .count = 1 }; - tlasInstanceInfo.flags = AccelerationStructureGeometryFlagBits::OPAQUE; + tlasInstanceInfo.flags = AccelerationStructureGeometryFlagBits::OPAQUE | + AccelerationStructureGeometryFlagBits::NO_DUPLICATE_ANY_HIT_INVOCATION; TlasBuildInfo tlasBuildInfo = { .instances = tlasInstanceInfo }; + tlasBuildInfo.flags = AccelerationStructureCreateFlagBits::PREFER_FAST_TRACE; AccelerationStructureBuildSizesInfo tlasSizeInfo = device->TlasSizeRequirements(tlasBuildInfo); // Create TLAS and scratch @@ -142,14 +145,14 @@ namespace VisualTests { instanceData.instanceCustomIndex = 0; instanceData.mask = 0xFF; instanceData.instanceShaderBindingTableRecordOffset = 0; - instanceData.flags = AccelerationStructureGeometryInstanceFlagBits::TRIANGLE_FACING_CULL_DISABLE; + instanceData.flags = AccelerationStructureGeometryInstanceFlagBits::TRIANGLE_FACING_CULL_DISABLE | + AccelerationStructureGeometryInstanceFlagBits::FORCE_OPAQUE; instanceData.blasAddress = blas->InstanceAddress(); - blasBuildInfo.geometries = geoArray; blasBuildInfo.dstBlas = blas->Internal(); blasBuildInfo.scratchBuffer = blasScratchBuffer->Internal(); - + tlasBuildInfo.instances = tlasInstanceInfo; tlasBuildInfo.dstTlas = tlas->Internal(); tlasBuildInfo.scratchBuffer = tlasScratchBuffer->Internal(); @@ -194,8 +197,6 @@ namespace VisualTests { .target = imageTarget, .clear = eastl::make_optional(eastl::array{ 0, 0, 0, 0 }), }); - task.UseAccelerationStructure({ .accelerationStructure = blas, .access = AccessConsts::FRAGMENT_SHADER_READ }); - task.UseAccelerationStructure({ .accelerationStructure = tlas, .access = AccessConsts::FRAGMENT_SHADER_READ }); }, [this](TaskCommandList& commands) { commands.SetRasterPipeline(pipeline); From da9c490a1ce1d0f5e41ba5e6e85418ea799e66bb Mon Sep 17 00:00:00 2001 From: ZilverBlade <90721817+ZilverBlade@users.noreply.github.com> Date: Sat, 15 Nov 2025 15:35:01 +0100 Subject: [PATCH 4/4] Updated rhi and fixed visual tests --- .gitmodules | 1 - ShockGraph/Resources.cpp | 6 +- ShockGraph/Resources.hpp | 7 ++- ShockGraph/TaskGraph.cpp | 7 --- ShockGraph/TaskResourceManager.cpp | 11 ++++ VisualTests/App.cpp | 27 ++++++++- VisualTests/App.hpp | 2 +- VisualTests/IVisualTest.hpp | 2 + VisualTests/Tests/AlphaBlending.hpp | 2 + VisualTests/Tests/AlphaToCoverage.cpp | 4 +- VisualTests/Tests/AlphaToCoverage.hpp | 2 + VisualTests/Tests/BlitImage.cpp | 13 +++++ VisualTests/Tests/BlitImage.hpp | 1 + VisualTests/Tests/ComputeUAV.hpp | 2 + VisualTests/Tests/DrawIndirect.hpp | 2 + VisualTests/Tests/GeometryShader.cpp | 1 + VisualTests/Tests/GeometryShader.hpp | 2 + VisualTests/Tests/HelloTexture.hpp | 2 + VisualTests/Tests/HelloTriangle.hpp | 2 + VisualTests/Tests/IndexBuffer.hpp | 2 + VisualTests/Tests/InstanceBuffer.hpp | 2 + VisualTests/Tests/MSAA.cpp | 1 + VisualTests/Tests/MSAA.hpp | 2 + VisualTests/Tests/PushConstants.hpp | 2 + VisualTests/Tests/RayQueryCompute.cpp | 43 ++++++++------ VisualTests/Tests/RayQueryCompute.hpp | 2 + VisualTests/Tests/RayQueryPixel.cpp | 56 ++++++++++++------- VisualTests/Tests/RayQueryPixel.hpp | 2 + VisualTests/Tests/SpecialisationConstants.hpp | 2 + VisualTests/Tests/TesselationShader.cpp | 1 + VisualTests/Tests/TesselationShader.hpp | 2 + VisualTests/Tests/UniformBuffer.hpp | 2 + VisualTests/Tests/UpdateBuffer.hpp | 2 + VisualTests/Tests/VertexBuffer.hpp | 2 + VisualTests/Tests/Wireframe.hpp | 2 + VisualTests/Tests/WireframeSmooth.hpp | 2 + VisualTests/main.cpp | 5 +- .../{RayQuery.slang => RayQueryPixel.slang} | 14 ++--- vendor/PyroPlatform | 2 +- vendor/PyroRHI | 2 +- 40 files changed, 180 insertions(+), 66 deletions(-) rename resources/VisualTests/Shaders/{RayQuery.slang => RayQueryPixel.slang} (86%) diff --git a/.gitmodules b/.gitmodules index 8622c57..4fb4348 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,4 +4,3 @@ [submodule "vendor/PyroRHI"] path = vendor/PyroRHI url = https://github.com/PyroshockStudios/PyroRHI.git - branch = ray_tracing_fixes diff --git a/ShockGraph/Resources.cpp b/ShockGraph/Resources.cpp index c5fee85..dc921e8 100644 --- a/ShockGraph/Resources.cpp +++ b/ShockGraph/Resources.cpp @@ -135,8 +135,10 @@ namespace PyroshockStudios { } else { Device()->Destroy(mBuffer); } - for (auto& buffer : mInFlightBuffers) { - Device()->Destroy(buffer); + if (this->mInfo.mode != TaskBufferMode::Host) { // Do not destroy these as they are copies of mBuffer! + for (auto& buffer : mInFlightBuffers) { + Device()->Destroy(buffer); + } } } SHOCKGRAPH_API u8* TaskBuffer_::MappedMemory() { diff --git a/ShockGraph/Resources.hpp b/ShockGraph/Resources.hpp index ffe7c8c..464bb45 100644 --- a/ShockGraph/Resources.hpp +++ b/ShockGraph/Resources.hpp @@ -186,9 +186,10 @@ namespace PyroshockStudios { enum struct TaskBufferMode : u32 { Default = 0, ///< Stored on Device, not accessible from CPU. - Dynamic = 1, ///< Stored on Device, with CPU write access, but GPU read-only. Optimised for fast GPU access, but writes may be slower. - HostDynamic = 2, ///< Stored on Host memory, with CPU read/write access, but GPU read-only. Optimised for fast CPU access, but reads may be slower. - Readback = 3, ///< Stored on Host memory, with CPU read access, but GPU write-only. + Host = 1, ///< Stored on Host, with CPU read/write access, but GPU read-only. Not to be confused with HostDynamic, as this does not allow for safe per-frame write access. + Dynamic = 2, ///< Stored on Device, with CPU write access, but GPU read-only. Optimised for fast GPU access, but writes may be slower. + HostDynamic = 3, ///< Stored on Host memory, with CPU read/write access, but GPU read-only. Optimised for fast CPU access, but reads may be slower. + Readback = 4, ///< Stored on Host memory, with CPU read access, but GPU write-only. }; struct TaskBufferInfo { diff --git a/ShockGraph/TaskGraph.cpp b/ShockGraph/TaskGraph.cpp index 0bf9514..84db623 100644 --- a/ShockGraph/TaskGraph.cpp +++ b/ShockGraph/TaskGraph.cpp @@ -723,16 +723,9 @@ namespace PyroshockStudios { task->mTimestampPool = mTimestampQueryPools[mFrameIndex]; wrapper.mCurrBindPoint = task->GetTask()->GetBindPoint(); - AccelerationStructureBarrierInfo barrier{}; - barrier.srcAccess = AccessConsts::READ_WRITE; - barrier.dstAccess = AccessConsts::READ_WRITE; - commandBuffer->AccelerationStructureBarrier(barrier); - task->PreExec(commandBuffer); task->GetTask()->ExecuteTask(wrapper); task->PostExec(commandBuffer); - - commandBuffer->AccelerationStructureBarrier(barrier); } ++batchIndex; } diff --git a/ShockGraph/TaskResourceManager.cpp b/ShockGraph/TaskResourceManager.cpp index f2a3312..e599beb 100644 --- a/ShockGraph/TaskResourceManager.cpp +++ b/ShockGraph/TaskResourceManager.cpp @@ -104,6 +104,14 @@ namespace PyroshockStudios { .allocationDomain = MemoryAllocationDomain::DeviceLocal, .name = info.name, }); + } else if (info.mode == TaskBufferMode::Host) { + buffer = mDevice->CreateBuffer({ + .size = info.size, + .usage = info.usage | extraRequiredFlags, + .initialLayout = BufferLayout::ReadOnly, + .allocationDomain = MemoryAllocationDomain::HostRandomWrite, + .name = info.name, + }); } else if (info.mode == TaskBufferMode::Dynamic) { buffer = mDevice->CreateBuffer({ .size = info.size, @@ -137,6 +145,9 @@ namespace PyroshockStudios { mPendingStagingUploads.emplace_back(eastl::move(uploadPair)); } + if (info.mode == TaskBufferMode::Host) { + buffersInFlight.resize(mFramesInFlight, buffer); + } TaskBuffer retBuffer = TaskBuffer::Create(this, info, eastl::move(buffer), eastl::move(buffersInFlight)); if (info.mode == TaskBufferMode::Dynamic || info.mode == TaskBufferMode::HostDynamic || info.mode == TaskBufferMode::Readback) { mDynamicBuffers.emplace_back(retBuffer.Get()); diff --git a/VisualTests/App.cpp b/VisualTests/App.cpp index cef4f9d..f1827a1 100644 --- a/VisualTests/App.cpp +++ b/VisualTests/App.cpp @@ -124,18 +124,38 @@ namespace VisualTests { void VisualTestApp::NextTest() { ReleaseTaskResources(); + u32 numTests = static_cast(mTests.size()); + tryagain: { mCurrentTest = (mCurrentTest + 1) % numTests; + if (!mTests[mCurrentTest]->TaskSupported(mRHIManager->GetRHIDevice())) { + goto tryagain; + } + } + RebuildTaskGraph(); } void VisualTestApp::PrevTest() { ReleaseTaskResources(); + u32 numTests = static_cast(mTests.size()); + tryagain: { mCurrentTest = (mCurrentTest + numTests - 1) % numTests; + if (!mTests[mCurrentTest]->TaskSupported(mRHIManager->GetRHIDevice())) { + goto tryagain; + } + } + RebuildTaskGraph(); } + void VisualTestApp::ReloadTest() { + ReleaseTaskResources(); + RebuildTaskGraph(); + } + + void VisualTestApp::ReleaseTaskResources() { if (mRHIManager->GetRHIDevice()) mRHIManager->GetRHIDevice()->WaitIdle(); @@ -188,6 +208,9 @@ namespace VisualTests { Logger::Info(gSGSink, "-- GRAPH FLUSHES TIMING -- {:.5f} ms", mTaskRenderGraph->GetMiscFlushesTimingsNs() / 1e6); Logger::Info(gSGSink, "-- TOTAL GRAPH TIMING -- {:.5f} ms", mTaskRenderGraph->GetGraphTimingsNs() / 1e6); } break; + case KeyCode::KeyR: + ReloadTest(); + break; } }); } @@ -251,11 +274,11 @@ namespace VisualTests { return; } - if(strcmp(mRHIManager->GetAttachedRHIInfo().info.shorthand, "dx12") == 0) { + if (strcmp(mRHIManager->GetAttachedRHIInfo().info.shorthand, "dx12") == 0) { mRHIManager->GetRHIDevice()->SetShaderModel(0x65); } - if(strcmp(mRHIManager->GetAttachedRHIInfo().info.shorthand, "vk13") == 0) { + if (strcmp(mRHIManager->GetAttachedRHIInfo().info.shorthand, "vk13") == 0) { mRHIManager->GetRHIDevice()->SetShaderModel(0x14); } } else { diff --git a/VisualTests/App.hpp b/VisualTests/App.hpp index 8b05cc7..f23e60a 100644 --- a/VisualTests/App.hpp +++ b/VisualTests/App.hpp @@ -101,7 +101,7 @@ namespace VisualTests { void PrevRHI(); void NextTest(); void PrevTest(); - + void ReloadTest(); private: void ReleaseTaskResources(); diff --git a/VisualTests/IVisualTest.hpp b/VisualTests/IVisualTest.hpp index 8a753a3..355e7da 100644 --- a/VisualTests/IVisualTest.hpp +++ b/VisualTests/IVisualTest.hpp @@ -59,6 +59,8 @@ namespace VisualTests { // Ownership is taken from these tests, do not try to access these tasks anymore inside IVisualTest PYRO_NODISCARD virtual eastl::span CreateTasks() = 0; + PYRO_NODISCARD virtual bool TaskSupported(IDevice* device) = 0; + PYRO_NODISCARD virtual bool UseTaskGraph() const = 0; PYRO_NODISCARD virtual TaskImage GetCompositeImageTaskGraph() = 0; PYRO_NODISCARD virtual Image GetCompositeImageRaw() = 0; diff --git a/VisualTests/Tests/AlphaBlending.hpp b/VisualTests/Tests/AlphaBlending.hpp index 4a3a33a..2352675 100644 --- a/VisualTests/Tests/AlphaBlending.hpp +++ b/VisualTests/Tests/AlphaBlending.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override { return true; } + private: TaskImage image; TaskColorTarget target; diff --git a/VisualTests/Tests/AlphaToCoverage.cpp b/VisualTests/Tests/AlphaToCoverage.cpp index ef62838..2e9f6a0 100644 --- a/VisualTests/Tests/AlphaToCoverage.cpp +++ b/VisualTests/Tests/AlphaToCoverage.cpp @@ -80,7 +80,8 @@ namespace VisualTests { imageMSAA = {}; target = {}; targetMSAA = {}; - vsh = {}; fsh = {}; + vsh = {}; + fsh = {}; pipeline = {}; } eastl::span AlphaToCoverage::CreateTasks() { @@ -102,4 +103,5 @@ namespace VisualTests { }; return tasks; } + bool AlphaToCoverage::TaskSupported(IDevice* device) { return device->Properties().msaaSupportColorTarget > RasterizationSamples::e1; } } // namespace VisualTests \ No newline at end of file diff --git a/VisualTests/Tests/AlphaToCoverage.hpp b/VisualTests/Tests/AlphaToCoverage.hpp index a77e040..de0034a 100644 --- a/VisualTests/Tests/AlphaToCoverage.hpp +++ b/VisualTests/Tests/AlphaToCoverage.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override; + private: TaskImage imageMSAA; TaskImage image; diff --git a/VisualTests/Tests/BlitImage.cpp b/VisualTests/Tests/BlitImage.cpp index dbc9bbb..8d7ec8b 100644 --- a/VisualTests/Tests/BlitImage.cpp +++ b/VisualTests/Tests/BlitImage.cpp @@ -133,7 +133,20 @@ namespace VisualTests { Filter::Linear }); // Now run all blits + + bool bFirst = true; for (auto& t : tests) { + if (!bFirst) { + // apparently blits must have write after write sync on vulkan... + commands->ImageBarrier({ + .image = blitInfo.dstImage, + .srcAccess = AccessConsts::BLIT_WRITE, + .dstAccess = AccessConsts::BLIT_WRITE, + .srcLayout = ImageLayout::BlitDst, + .dstLayout = ImageLayout::BlitDst, + }); + } + bFirst = false; blitInfo.srcImageBox = { .x = t.srcRect.x, .y = t.srcRect.y, diff --git a/VisualTests/Tests/BlitImage.hpp b/VisualTests/Tests/BlitImage.hpp index f0fcb2d..0a32115 100644 --- a/VisualTests/Tests/BlitImage.hpp +++ b/VisualTests/Tests/BlitImage.hpp @@ -35,6 +35,7 @@ namespace VisualTests { bool UseTaskGraph() const override { return true; } TaskImage GetCompositeImageTaskGraph() override { return blitImage; } Image GetCompositeImageRaw() override { return blitImage->Internal(); } + bool TaskSupported(IDevice* device) override { return true; } private: TaskImage image; diff --git a/VisualTests/Tests/ComputeUAV.hpp b/VisualTests/Tests/ComputeUAV.hpp index 99a96bd..4a7b3ed 100644 --- a/VisualTests/Tests/ComputeUAV.hpp +++ b/VisualTests/Tests/ComputeUAV.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override { return true; } + private: TaskImage image; TaskImage depth; diff --git a/VisualTests/Tests/DrawIndirect.hpp b/VisualTests/Tests/DrawIndirect.hpp index 2d7c1c7..3951650 100644 --- a/VisualTests/Tests/DrawIndirect.hpp +++ b/VisualTests/Tests/DrawIndirect.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override { return true; } + private: TaskImage image; TaskBuffer vbo; diff --git a/VisualTests/Tests/GeometryShader.cpp b/VisualTests/Tests/GeometryShader.cpp index baa3fcf..02f314c 100644 --- a/VisualTests/Tests/GeometryShader.cpp +++ b/VisualTests/Tests/GeometryShader.cpp @@ -80,4 +80,5 @@ namespace VisualTests { }; return tasks; } + bool GeometryShader::TaskSupported(IDevice* device) { return device->Features().bGeometryShaders; } } // namespace VisualTests \ No newline at end of file diff --git a/VisualTests/Tests/GeometryShader.hpp b/VisualTests/Tests/GeometryShader.hpp index 37060e5..b6448f3 100644 --- a/VisualTests/Tests/GeometryShader.hpp +++ b/VisualTests/Tests/GeometryShader.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override; + private: TaskImage image; TaskColorTarget target; diff --git a/VisualTests/Tests/HelloTexture.hpp b/VisualTests/Tests/HelloTexture.hpp index 384674f..60818b1 100644 --- a/VisualTests/Tests/HelloTexture.hpp +++ b/VisualTests/Tests/HelloTexture.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override { return true; } + private: TaskImage image; TaskImage texture; diff --git a/VisualTests/Tests/HelloTriangle.hpp b/VisualTests/Tests/HelloTriangle.hpp index 8132dfb..a4fd985 100644 --- a/VisualTests/Tests/HelloTriangle.hpp +++ b/VisualTests/Tests/HelloTriangle.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override { return true; } + private: TaskImage image; TaskColorTarget target; diff --git a/VisualTests/Tests/IndexBuffer.hpp b/VisualTests/Tests/IndexBuffer.hpp index 304be00..44ed2a0 100644 --- a/VisualTests/Tests/IndexBuffer.hpp +++ b/VisualTests/Tests/IndexBuffer.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override { return true; } + private: TaskImage image; TaskBuffer vbo; diff --git a/VisualTests/Tests/InstanceBuffer.hpp b/VisualTests/Tests/InstanceBuffer.hpp index a5eb3ef..5c02bbd 100644 --- a/VisualTests/Tests/InstanceBuffer.hpp +++ b/VisualTests/Tests/InstanceBuffer.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override { return true; } + private: TaskImage image; TaskBuffer vbo; diff --git a/VisualTests/Tests/MSAA.cpp b/VisualTests/Tests/MSAA.cpp index 2889b70..c391f62 100644 --- a/VisualTests/Tests/MSAA.cpp +++ b/VisualTests/Tests/MSAA.cpp @@ -142,4 +142,5 @@ namespace VisualTests { }; return tasks; } + bool MSAA::TaskSupported(IDevice* device) { return device->Properties().msaaSupportColorTarget > RasterizationSamples::e1; } } // namespace VisualTests \ No newline at end of file diff --git a/VisualTests/Tests/MSAA.hpp b/VisualTests/Tests/MSAA.hpp index 60f61cc..41fd884 100644 --- a/VisualTests/Tests/MSAA.hpp +++ b/VisualTests/Tests/MSAA.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override; + private: TaskImage imageMSAA; TaskImage image; diff --git a/VisualTests/Tests/PushConstants.hpp b/VisualTests/Tests/PushConstants.hpp index 77b6325..32b0ad3 100644 --- a/VisualTests/Tests/PushConstants.hpp +++ b/VisualTests/Tests/PushConstants.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override { return true; } + private: TaskImage image; TaskColorTarget target; diff --git a/VisualTests/Tests/RayQueryCompute.cpp b/VisualTests/Tests/RayQueryCompute.cpp index 78ce046..c73b8ce 100644 --- a/VisualTests/Tests/RayQueryCompute.cpp +++ b/VisualTests/Tests/RayQueryCompute.cpp @@ -149,23 +149,14 @@ namespace VisualTests { } eastl::span RayQueryCompute::CreateTasks() { - // Task 1: Build BLAS/TLAS - GenericTask* buildTask = new CustomCallbackTask( - { .name = "Build Acceleration Structures", .color = LabelColor::YELLOW }, + // Task 1: Build BLAS + GenericTask* buildBlasTask = new CustomCallbackTask( + { .name = "Build Bottom Level Acceleration Structures", .color = LabelColor::YELLOW }, [this](CustomTask& task) { // declare buffer usage so task graph orders correctly - task.UseBuffer({ .buffer = vertexBuffer, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ }); - task.UseBuffer({ .buffer = indexBuffer, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ }); - task.UseBuffer({ .buffer = instanceBuffer, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ }); - task.UseBuffer({ .buffer = blasScratchBuffer, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ_WRITE }); - task.UseBuffer({ .buffer = tlasScratchBuffer, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ_WRITE }); task.UseAccelerationStructure({ .accelerationStructure = blas, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ_WRITE }); - task.UseAccelerationStructure({ .accelerationStructure = tlas, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ_WRITE }); }, [this](ICommandBuffer* commands) { - if (bBuilt) - return; - bBuilt = true; // Prepare RHI build infos BlasTriangleGeometryInfo triGeo{}; triGeo.flags = AccelerationStructureGeometryFlagBits::OPAQUE | AccelerationStructureGeometryFlagBits::NO_DUPLICATE_ANY_HIT_INVOCATION; @@ -183,20 +174,35 @@ namespace VisualTests { blasBuildInfo.dstBlas = blas->Internal(); blasBuildInfo.scratchBuffer = blasScratchBuffer->Internal(); + BuildAccelerationStructuresInfo buildInfo = { + .blasBuildInfos = eastl::span(&blasBuildInfo, 1), + }; + + commands->BuildAccelerationStructures(buildInfo); + }, + TaskType::Transfer); + // Task 2: build Tlas + GenericTask* buildTlasTask = new CustomCallbackTask( + { .name = "Build Top Level Acceleration Structures", .color = LabelColor::YELLOW }, + [this](CustomTask& task) { + // declare buffer usage so task graph orders correctly + task.UseAccelerationStructure({ .accelerationStructure = blas, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ }); + task.UseAccelerationStructure({ .accelerationStructure = tlas, .access = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ_WRITE }); + }, + [this](ICommandBuffer* commands) { TlasInstanceInfo tlasInstanceInfo = { .data = instanceBuffer->Internal(), .count = 1 }; tlasInstanceInfo.flags = AccelerationStructureGeometryFlagBits::OPAQUE | AccelerationStructureGeometryFlagBits::NO_DUPLICATE_ANY_HIT_INVOCATION; - + TlasBuildInfo tlasBuildInfo{}; tlasBuildInfo.instances = tlasInstanceInfo; tlasBuildInfo.dstTlas = tlas->Internal(); tlasBuildInfo.scratchBuffer = tlasScratchBuffer->Internal(); - BuildAccelerationStructuresInfo buildAllInfo = { + BuildAccelerationStructuresInfo buildInfo = { .tlasBuildInfos = eastl::span(&tlasBuildInfo, 1), - .blasBuildInfos = eastl::span(&blasBuildInfo, 1), }; - commands->BuildAccelerationStructures(buildAllInfo); + commands->BuildAccelerationStructures(buildInfo); }, TaskType::Transfer); @@ -205,7 +211,6 @@ namespace VisualTests { { .name = "RayQuery Compute Dispatch", .color = LabelColor::YELLOW }, [this](ComputeTask& task) { task.UseImage({ .image = image, .access = AccessConsts::COMPUTE_SHADER_WRITE }); - task.UseAccelerationStructure({ .accelerationStructure = blas, .access = AccessConsts::COMPUTE_SHADER_READ }); task.UseAccelerationStructure({ .accelerationStructure = tlas, .access = AccessConsts::COMPUTE_SHADER_READ }); }, [this](TaskCommandList& commands) { @@ -221,9 +226,11 @@ namespace VisualTests { commands.Dispatch({ .x = gx, .y = gy }); }); - tasks = { buildTask, dispatchTask }; + tasks = { buildBlasTask, buildTlasTask, dispatchTask }; return tasks; } + bool RayQueryCompute::TaskSupported(IDevice* device) { return device->Features().bAccelerationStructureBuild&& + device->Features().bRayQueries; } } // namespace VisualTests diff --git a/VisualTests/Tests/RayQueryCompute.hpp b/VisualTests/Tests/RayQueryCompute.hpp index ad95bdc..1e3e012 100644 --- a/VisualTests/Tests/RayQueryCompute.hpp +++ b/VisualTests/Tests/RayQueryCompute.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override; + private: TaskImage image; UnorderedAccessId imageUav; diff --git a/VisualTests/Tests/RayQueryPixel.cpp b/VisualTests/Tests/RayQueryPixel.cpp index 81127e7..b8d84e5 100644 --- a/VisualTests/Tests/RayQueryPixel.cpp +++ b/VisualTests/Tests/RayQueryPixel.cpp @@ -40,10 +40,10 @@ namespace VisualTests { .name = "Hello Texture RT", }); - vsh = info.shaderCompiler.CompileShaderFromFile("resources/VisualTests/Shaders/RayQuery.slang", - { .stage = ShaderStage::Vertex, .entryPoint = "vertexMain", .name = "RayQuery Vsh" }); - fsh = info.shaderCompiler.CompileShaderFromFile("resources/VisualTests/Shaders/RayQuery.slang", - { .stage = ShaderStage::Fragment, .entryPoint = "fragmentMain", .name = "RayQuery Fsh" }); + vsh = info.shaderCompiler.CompileShaderFromFile("resources/VisualTests/Shaders/RayQueryPixel.slang", + { .stage = ShaderStage::Vertex, .entryPoint = "vertexMain", .name = "RayQueryPixel Vsh" }); + fsh = info.shaderCompiler.CompileShaderFromFile("resources/VisualTests/Shaders/RayQueryPixel.slang", + { .stage = ShaderStage::Fragment, .entryPoint = "fragmentMain", .name = "RayQueryPixel Fsh" }); pipeline = info.resourceManager.CreateRasterPipeline( { .colorTargetStates = { { .format = image->Info().format } }, @@ -59,7 +59,7 @@ namespace VisualTests { { .size = sizeof(SimpleVertex) * 4, .usage = BufferUsageFlagBits::BLAS_GEOMETRY_BUFFER, - .mode = TaskBufferMode::HostDynamic, + .mode = TaskBufferMode::Host, .name = "RT Vertices", }); @@ -67,7 +67,7 @@ namespace VisualTests { { .size = sizeof(u32) * 6, .usage = BufferUsageFlagBits::BLAS_GEOMETRY_BUFFER, - .mode = TaskBufferMode::HostDynamic, + .mode = TaskBufferMode::Host, .name = "RT Indices", }); @@ -76,8 +76,8 @@ namespace VisualTests { // Prepare RHI Blas geometry info BlasTriangleGeometryInfo triGeo{}; - triGeo.flags = AccelerationStructureGeometryFlagBits::OPAQUE | - AccelerationStructureGeometryFlagBits::NO_DUPLICATE_ANY_HIT_INVOCATION; + triGeo.flags = AccelerationStructureGeometryFlagBits::OPAQUE | + AccelerationStructureGeometryFlagBits::NO_DUPLICATE_ANY_HIT_INVOCATION; triGeo.vertexFormat = Format::RGB32Sfloat; triGeo.indexType = IndexType::Uint32; triGeo.vertexBuffer = vertexBuffer->Internal(); @@ -85,7 +85,7 @@ namespace VisualTests { triGeo.vertexStride = sizeof(SimpleVertex); triGeo.vertexCount = 4; triGeo.indexCount = 6; - + eastl::array geoArray = { triGeo }; BlasBuildInfo blasBuildInfo = { .geometries = geoArray }; blasBuildInfo.flags = AccelerationStructureCreateFlagBits::PREFER_FAST_TRACE; @@ -106,14 +106,14 @@ namespace VisualTests { { .size = sizeof(BlasInstanceData), .usage = BufferUsageFlagBits::BLAS_INSTANCE_BUFFER, - .mode = TaskBufferMode::HostDynamic, + .mode = TaskBufferMode::Host, .name = "RT Instance Buffer", }); // TLAS size requirements TlasInstanceInfo tlasInstanceInfo = { .data = instanceBuffer->Internal(), .count = 1 }; tlasInstanceInfo.flags = AccelerationStructureGeometryFlagBits::OPAQUE | - AccelerationStructureGeometryFlagBits::NO_DUPLICATE_ANY_HIT_INVOCATION; + AccelerationStructureGeometryFlagBits::NO_DUPLICATE_ANY_HIT_INVOCATION; TlasBuildInfo tlasBuildInfo = { .instances = tlasInstanceInfo }; tlasBuildInfo.flags = AccelerationStructureCreateFlagBits::PREFER_FAST_TRACE; AccelerationStructureBuildSizesInfo tlasSizeInfo = device->TlasSizeRequirements(tlasBuildInfo); @@ -145,25 +145,41 @@ namespace VisualTests { instanceData.instanceCustomIndex = 0; instanceData.mask = 0xFF; instanceData.instanceShaderBindingTableRecordOffset = 0; - instanceData.flags = AccelerationStructureGeometryInstanceFlagBits::TRIANGLE_FACING_CULL_DISABLE | - AccelerationStructureGeometryInstanceFlagBits::FORCE_OPAQUE; + instanceData.flags = AccelerationStructureGeometryInstanceFlagBits::TRIANGLE_FACING_CULL_DISABLE | + AccelerationStructureGeometryInstanceFlagBits::FORCE_OPAQUE; instanceData.blasAddress = blas->InstanceAddress(); blasBuildInfo.geometries = geoArray; blasBuildInfo.dstBlas = blas->Internal(); blasBuildInfo.scratchBuffer = blasScratchBuffer->Internal(); - + tlasBuildInfo.instances = tlasInstanceInfo; tlasBuildInfo.dstTlas = tlas->Internal(); tlasBuildInfo.scratchBuffer = tlasScratchBuffer->Internal(); - BuildAccelerationStructuresInfo buildAllInfo = { - .tlasBuildInfos = eastl::span(&tlasBuildInfo, 1), - .blasBuildInfos = eastl::span(&blasBuildInfo, 1), - }; + ICommandBuffer* singleTimeCommands = device->GetPresentQueue()->GetCommandBuffer({ .name = "Single time build commands" }); - singleTimeCommands->BuildAccelerationStructures(buildAllInfo); + // build blas first + { + BuildAccelerationStructuresInfo buildInfo = { + .blasBuildInfos = eastl::span(&blasBuildInfo, 1), + }; + singleTimeCommands->BuildAccelerationStructures(buildInfo); + } + // wait for blas to finish + singleTimeCommands->AccelerationStructureBarrier({ + .accelerationStructure = blasBuildInfo.dstBlas, + .srcAccess = AccessConsts::ACCELERATION_STRUCTURE_BUILD_WRITE, + .dstAccess = AccessConsts::ACCELERATION_STRUCTURE_BUILD_READ, + }); + // then build tlas + { + BuildAccelerationStructuresInfo buildInfo = { + .tlasBuildInfos = eastl::span(&tlasBuildInfo, 1), + }; + singleTimeCommands->BuildAccelerationStructures(buildInfo); + } singleTimeCommands->Complete(); device->GetPresentQueue()->SubmitCommandBuffer(singleTimeCommands); device->SubmitQueue({ .queue = device->GetPresentQueue() }); @@ -211,4 +227,6 @@ namespace VisualTests { return tasks; } + bool RayQueryPixel::TaskSupported(IDevice* device) { return device->Features().bAccelerationStructureBuild && + device->Features().bRayQueries; } } // namespace VisualTests diff --git a/VisualTests/Tests/RayQueryPixel.hpp b/VisualTests/Tests/RayQueryPixel.hpp index 37eb2a1..4b11bb4 100644 --- a/VisualTests/Tests/RayQueryPixel.hpp +++ b/VisualTests/Tests/RayQueryPixel.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override; + private: TaskImage image; TaskColorTarget imageTarget; diff --git a/VisualTests/Tests/SpecialisationConstants.hpp b/VisualTests/Tests/SpecialisationConstants.hpp index 54e5d41..17e60d3 100644 --- a/VisualTests/Tests/SpecialisationConstants.hpp +++ b/VisualTests/Tests/SpecialisationConstants.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override { return true; } + private: TaskImage image; TaskColorTarget target; diff --git a/VisualTests/Tests/TesselationShader.cpp b/VisualTests/Tests/TesselationShader.cpp index 71a2b27..b4f96d0 100644 --- a/VisualTests/Tests/TesselationShader.cpp +++ b/VisualTests/Tests/TesselationShader.cpp @@ -87,4 +87,5 @@ namespace VisualTests { }; return tasks; } + bool TesselationShader::TaskSupported(IDevice* device) { return device->Features().bTesselationShaders; } } // namespace VisualTests \ No newline at end of file diff --git a/VisualTests/Tests/TesselationShader.hpp b/VisualTests/Tests/TesselationShader.hpp index 848db1a..ae03046 100644 --- a/VisualTests/Tests/TesselationShader.hpp +++ b/VisualTests/Tests/TesselationShader.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override; + private: TaskImage image; TaskColorTarget target; diff --git a/VisualTests/Tests/UniformBuffer.hpp b/VisualTests/Tests/UniformBuffer.hpp index 2663a32..043097b 100644 --- a/VisualTests/Tests/UniformBuffer.hpp +++ b/VisualTests/Tests/UniformBuffer.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override { return true; } + private: TaskImage image; TaskBuffer ubo; diff --git a/VisualTests/Tests/UpdateBuffer.hpp b/VisualTests/Tests/UpdateBuffer.hpp index 8eacb4e..d99f767 100644 --- a/VisualTests/Tests/UpdateBuffer.hpp +++ b/VisualTests/Tests/UpdateBuffer.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override { return true; } + private: TaskImage image; TaskBuffer ubo; diff --git a/VisualTests/Tests/VertexBuffer.hpp b/VisualTests/Tests/VertexBuffer.hpp index a8166e5..b0b30ae 100644 --- a/VisualTests/Tests/VertexBuffer.hpp +++ b/VisualTests/Tests/VertexBuffer.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override { return true; } + private: TaskImage image; TaskBuffer vbo; diff --git a/VisualTests/Tests/Wireframe.hpp b/VisualTests/Tests/Wireframe.hpp index 50926c6..36691ed 100644 --- a/VisualTests/Tests/Wireframe.hpp +++ b/VisualTests/Tests/Wireframe.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override { return true; } + private: TaskImage image; TaskColorTarget target; diff --git a/VisualTests/Tests/WireframeSmooth.hpp b/VisualTests/Tests/WireframeSmooth.hpp index d1a014b..fd4ad79 100644 --- a/VisualTests/Tests/WireframeSmooth.hpp +++ b/VisualTests/Tests/WireframeSmooth.hpp @@ -36,6 +36,8 @@ namespace VisualTests { TaskImage GetCompositeImageTaskGraph() override { return image; } Image GetCompositeImageRaw() override { return image->Internal(); } + bool TaskSupported(IDevice* device) override { return true; } + private: TaskImage image; TaskColorTarget target; diff --git a/VisualTests/main.cpp b/VisualTests/main.cpp index 8f72c18..fb66191 100644 --- a/VisualTests/main.cpp +++ b/VisualTests/main.cpp @@ -65,12 +65,11 @@ using namespace VisualTests; * Next Test: > * Prev Test: < * Print Task Timings: P +* Reload Test: R */ int main(i32 argc, char** argv) { VisualTestApp* app = new VisualTestApp(); - app->RegisterTest(); - app->RegisterTest(); app->RegisterTest(); app->RegisterTest(); app->RegisterTest(); @@ -90,6 +89,8 @@ int main(i32 argc, char** argv) { app->RegisterTest(); app->RegisterTest(); app->RegisterTest(); + app->RegisterTest(); + app->RegisterTest(); app->Run(); diff --git a/resources/VisualTests/Shaders/RayQuery.slang b/resources/VisualTests/Shaders/RayQueryPixel.slang similarity index 86% rename from resources/VisualTests/Shaders/RayQuery.slang rename to resources/VisualTests/Shaders/RayQueryPixel.slang index 54e3e99..c46c744 100644 --- a/resources/VisualTests/Shaders/RayQuery.slang +++ b/resources/VisualTests/Shaders/RayQueryPixel.slang @@ -49,28 +49,26 @@ float4 fragmentMain(VertexOutput input) : SV_Target RayDesc ray; ray.Origin = origin; ray.Direction = direction; - ray.TMin = 0.1; + ray.TMin = 0.01; ray.TMax = 100.0; - RayQuery query; + RayQuery query; query.TraceRayInline( gTlas[tlasIndex], - RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_FORCE_OPAQUE, + 0, 0xFF, ray ); uint hit = 0; - query.Proceed(); + while (query.Proceed()) { + hit = 1; + } if (query.CommittedStatus() == COMMITTED_TRIANGLE_HIT) { hit = 1; } - else - { - hit = 0; // No committed hit found - } return hit == 1 ? float4(1.0, 0.0, 0.0, 1.0) : float4(0.0, 0.0, 0.0, 1.0); // float4(origin, 1.0); } diff --git a/vendor/PyroPlatform b/vendor/PyroPlatform index 61cc188..e45ecb7 160000 --- a/vendor/PyroPlatform +++ b/vendor/PyroPlatform @@ -1 +1 @@ -Subproject commit 61cc188d8f5a8eb3e1c1765851a1239920d7f952 +Subproject commit e45ecb74027cf3a9e9d97aa8815a4af48cc4013d diff --git a/vendor/PyroRHI b/vendor/PyroRHI index 9a25fc5..25c0c14 160000 --- a/vendor/PyroRHI +++ b/vendor/PyroRHI @@ -1 +1 @@ -Subproject commit 9a25fc5346b4b47aaf02739e735d561c681fc2f9 +Subproject commit 25c0c14e9fabe7a468efd28884d4250189bab136