Fix rendering various visuals on WebGPU

This commit is contained in:
2026-03-02 20:36:33 +01:00
parent b36c757753
commit b191d3918e
36 changed files with 458 additions and 220 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
"Major": 1,
"Minor": 12,
"Revision": 0,
"Build": 6905
"Build": 6906
},
"Company": "Flax",
"Copyright": "Copyright (c) 2012-2026 Wojciech Figat. All rights reserved.",
-2
View File
@@ -391,8 +391,6 @@ bool GPUDevice::Init()
_res->TasksManager.SetExecutor(CreateTasksExecutor());
LOG(Info, "Total graphics memory: {0}", Utilities::BytesToText(TotalGraphicsMemory));
if (!Limits.HasCompute)
LOG(Warning, "Compute Shaders are not supported");
for (const auto& videoOutput : VideoOutputs)
LOG(Info, "Video output '{0}' {1}x{2} {3} Hz", videoOutput.Name, videoOutput.Width, videoOutput.Height, videoOutput.RefreshRate);
Engine::RequestingExit.Bind<GPUDevice, &GPUDevice::OnRequestingExit>(this);
@@ -10,7 +10,7 @@
/// <summary>
/// Current materials shader version.
/// </summary>
#define MATERIAL_GRAPH_VERSION 182
#define MATERIAL_GRAPH_VERSION 183
class Material;
class GPUShader;
+7 -10
View File
@@ -87,12 +87,10 @@ GPUTexture* RenderBuffers::RequestHalfResDepth(GPUContext* context)
if (!MultiScaler::Instance()->IsReady())
return DepthBuffer;
const int32 halfDepthWidth = RenderTools::GetResolution(_width, ResolutionMode::Half);
const int32 halfDepthHeight = RenderTools::GetResolution(_height, ResolutionMode::Half);
const PixelFormat halfDepthFormat = GPU_DEPTH_BUFFER_PIXEL_FORMAT;
auto tempDesc = GPUTextureDescription::New2D(halfDepthWidth, halfDepthHeight, halfDepthFormat);
if (EnumHasAnyFlags(DepthBuffer->Flags(), GPUTextureFlags::ReadOnlyDepthView))
tempDesc.Flags = GPUTextureFlags::ShaderResource | GPUTextureFlags::DepthStencil | GPUTextureFlags::ReadOnlyDepthView;
auto format = GPU_DEPTH_BUFFER_PIXEL_FORMAT;
auto width = RenderTools::GetResolution(_width, ResolutionMode::Half);
auto height = RenderTools::GetResolution(_height, ResolutionMode::Half);
auto tempDesc = GPUTextureDescription::New2D(width, height, format, DepthBuffer->Flags());
LastFrameHalfResDepth = currentFrame;
if (HalfResDepth == nullptr)
@@ -101,7 +99,7 @@ GPUTexture* RenderBuffers::RequestHalfResDepth(GPUContext* context)
HalfResDepth = RenderTargetPool::Get(tempDesc);
RENDER_TARGET_POOL_SET_NAME(HalfResDepth, "HalfResDepth");
}
else if (HalfResDepth->Width() != halfDepthWidth || HalfResDepth->Height() != halfDepthHeight || HalfResDepth->Format() != halfDepthFormat)
else if (HalfResDepth->Width() != width || HalfResDepth->Height() != height || HalfResDepth->Format() != format)
{
// Wrong size buffer
RenderTargetPool::Release(HalfResDepth);
@@ -110,7 +108,7 @@ GPUTexture* RenderBuffers::RequestHalfResDepth(GPUContext* context)
}
// Generate depth
MultiScaler::Instance()->DownscaleDepth(context, halfDepthWidth, halfDepthHeight, DepthBuffer, HalfResDepth->View());
MultiScaler::Instance()->DownscaleDepth(context, width, height, DepthBuffer, HalfResDepth->View());
return HalfResDepth;
}
@@ -126,8 +124,7 @@ GPUTexture* RenderBuffers::RequestHiZ(GPUContext* context, bool fullRes, int32 m
LastFrameHiZ = currentFrame;
// Allocate or resize buffer (with full mip-chain)
// TODO: migrate to inverse depth and try using r16 again as default (should have no artifacts anymore)
auto format = PLATFORM_WEB || PLATFORM_ANDROID || PLATFORM_IOS || PLATFORM_SWITCH ? PixelFormat::R16_UInt : PixelFormat::R32_Float;
auto format = PixelFormat::R32_Float;
auto width = fullRes ? _width : Math::Max(_width >> 1, 1);
auto height = fullRes ? _height : Math::Max(_height >> 1, 1);
auto desc = GPUTextureDescription::New2D(width, height, mipLevels, format, GPUTextureFlags::ShaderResource);
@@ -60,6 +60,8 @@ GPUContextWebGPU::GPUContextWebGPU(GPUDeviceWebGPU* device)
GPUContextWebGPU::~GPUContextWebGPU()
{
if (Encoder)
Flush();
CHECK(Encoder == nullptr);
}
@@ -120,6 +122,10 @@ void GPUContextWebGPU::FrameEnd()
void GPUContextWebGPU::EventBegin(const Char* name)
{
// Cannot insert commands in encoder during render pass
if (_renderPass)
EndRenderPass();
StringAsANSI<> nameAnsi(name);
wgpuCommandEncoderPushDebugGroup(Encoder, { nameAnsi.Get(), (size_t)nameAnsi.Length() });
}
@@ -252,17 +258,19 @@ void GPUContextWebGPU::SetStencilRef(uint32 value)
void GPUContextWebGPU::ResetSR()
{
_bindGroupDirty = true;
Platform::MemoryClear(_shaderResources, sizeof(_shaderResources));
}
void GPUContextWebGPU::ResetUA()
{
_bindGroupDirty = true;
Platform::MemoryClear(_storageResources, sizeof(_storageResources));
}
void GPUContextWebGPU::ResetCB()
{
_bindGroupDirty = false;
_bindGroupDirty = true;
Platform::MemoryClear(_constantBuffers, sizeof(_constantBuffers));
}
@@ -425,14 +433,14 @@ void GPUContextWebGPU::EndQuery(uint64 queryID)
void GPUContextWebGPU::SetViewport(const Viewport& viewport)
{
_viewport = viewport;
if (_renderPass)
if (_renderPass && !_renderPassDirty)
wgpuRenderPassEncoderSetViewport(_renderPass, viewport.X, viewport.Y, viewport.Width, viewport.Height, viewport.MinDepth, viewport.MaxDepth);
}
void GPUContextWebGPU::SetScissor(const Rectangle& scissorRect)
{
_scissorRect = scissorRect;
if (_renderPass)
if (_renderPass && !_renderPassDirty)
wgpuRenderPassEncoderSetScissorRect(_renderPass, (uint32_t)scissorRect.GetX(), (uint32_t)scissorRect.GetY(), (uint32_t)scissorRect.GetWidth(), (uint32_t)scissorRect.GetHeight());
}
@@ -490,6 +498,7 @@ void GPUContextWebGPU::Flush()
WGPUCommandBufferDescriptor commandBufferDesc = WGPU_COMMAND_BUFFER_DESCRIPTOR_INIT;
WGPUCommandBuffer commandBuffer = wgpuCommandEncoderFinish(Encoder, &commandBufferDesc);
wgpuCommandEncoderRelease(Encoder);
Encoder = nullptr;
if (commandBuffer)
{
wgpuQueueSubmit(_device->Queue, 1, &commandBuffer);
@@ -567,8 +576,7 @@ void GPUContextWebGPU::CopyTexture(GPUTexture* dstResource, uint32 dstSubresourc
ASSERT(dstResource && srcResource);
auto srcTextureWebGPU = (GPUTextureWebGPU*)srcResource;
auto dstTextureWebGPU = (GPUTextureWebGPU*)dstResource;
ASSERT_LOW_LAYER(dstTextureWebGPU->Texture && wgpuTextureGetUsage(dstTextureWebGPU->Texture) & WGPUTextureUsage_CopyDst);
ASSERT_LOW_LAYER(srcTextureWebGPU->Texture && wgpuTextureGetUsage(srcTextureWebGPU->Texture) & WGPUTextureUsage_CopySrc);
ASSERT_LOW_LAYER(dstTextureWebGPU->Texture && srcTextureWebGPU->Texture);
const int32 srcMipIndex = srcSubresource % srcTextureWebGPU->MipLevels();
const int32 dstMipIndex = dstSubresource % srcTextureWebGPU->MipLevels();
@@ -578,18 +586,45 @@ void GPUContextWebGPU::CopyTexture(GPUTexture* dstResource, uint32 dstSubresourc
int32 srcMipWidth, srcMipHeight, srcMipDepth;
srcTextureWebGPU->GetMipSize(srcMipIndex, srcMipWidth, srcMipHeight, srcMipDepth);
WGPUTexelCopyTextureInfo srcInfo = WGPU_TEXEL_COPY_TEXTURE_INFO_INIT;
srcInfo.texture = srcTextureWebGPU->Texture;
srcInfo.mipLevel = srcMipIndex;
srcInfo.origin.z = srcArrayIndex;
srcInfo.aspect = WGPUTextureAspect_All;
WGPUTexelCopyTextureInfo dstInfo = WGPU_TEXEL_COPY_TEXTURE_INFO_INIT;
dstInfo.texture = dstTextureWebGPU->Texture;
dstInfo.mipLevel = dstMipIndex;
dstInfo.origin = { dstX, dstY, dstZ + dstArrayIndex };
dstInfo.aspect = WGPUTextureAspect_All;
WGPUExtent3D copySize = { (uint32_t)srcMipWidth, (uint32_t)srcMipHeight, (uint32_t)srcMipDepth };
wgpuCommandEncoderCopyTextureToTexture(Encoder, &srcInfo, &dstInfo, &copySize);
if (dstTextureWebGPU->Usage & WGPUTextureUsage_CopyDst && srcTextureWebGPU->Usage & WGPUTextureUsage_CopySrc)
{
// Direct copy
WGPUTexelCopyTextureInfo srcInfo = WGPU_TEXEL_COPY_TEXTURE_INFO_INIT;
srcInfo.texture = srcTextureWebGPU->Texture;
srcInfo.mipLevel = srcMipIndex;
srcInfo.origin.z = srcArrayIndex;
srcInfo.aspect = WGPUTextureAspect_All;
WGPUTexelCopyTextureInfo dstInfo = WGPU_TEXEL_COPY_TEXTURE_INFO_INIT;
dstInfo.texture = dstTextureWebGPU->Texture;
dstInfo.mipLevel = dstMipIndex;
dstInfo.origin = { dstX, dstY, dstZ + dstArrayIndex };
dstInfo.aspect = WGPUTextureAspect_All;
WGPUExtent3D copySize = { (uint32_t)srcMipWidth, (uint32_t)srcMipHeight, (uint32_t)srcMipDepth };
wgpuCommandEncoderCopyTextureToTexture(Encoder, &srcInfo, &dstInfo, &copySize);
}
else if (dstTextureWebGPU->Usage & WGPUTextureUsage_RenderAttachment && srcTextureWebGPU->Usage & WGPUTextureUsage_TextureBinding)
{
// Copy via drawing
ResetRenderTarget();
SetViewportAndScissors(srcMipWidth, srcMipHeight);
SetState(_device->GetCopyLinearPS());
if (srcSubresource == 0 && dstSubresource == 0)
{
SetRenderTarget(dstTextureWebGPU->View(0));
BindSR(0, srcTextureWebGPU->View(0));
}
else
{
ASSERT(dstTextureWebGPU->HasPerMipViews() && srcResource->HasPerMipViews());
SetRenderTarget(dstTextureWebGPU->View(dstArrayIndex, dstMipIndex));
BindSR(0, srcTextureWebGPU->View(srcArrayIndex, srcMipIndex));
}
DrawFullscreenTriangle();
}
else
{
LOG(Fatal, "Cannot copy texture {} to {}", srcTextureWebGPU->GetDescription().ToString(), dstTextureWebGPU->GetDescription().ToString());
}
}
void GPUContextWebGPU::ResetCounter(GPUBuffer* buffer)
@@ -615,9 +650,15 @@ void GPUContextWebGPU::CopyResource(GPUResource* dstResource, GPUResource* srcRe
{
// Texture -> Texture
ASSERT(srcTexture->MipLevels() == dstTexture->MipLevels());
ASSERT(srcTexture->ArraySize() == 1); // TODO: implement copying texture arrays
for (int32 mipLevel = 0; mipLevel < srcTexture->MipLevels(); mipLevel++)
CopyTexture(dstTexture, mipLevel, 0, 0, 0, srcTexture, mipLevel);
ASSERT(srcTexture->ArraySize() == dstTexture->ArraySize());
for (int32 arraySlice = 0; arraySlice < srcTexture->ArraySize(); arraySlice++)
{
for (int32 mipLevel = 0; mipLevel < srcTexture->MipLevels(); mipLevel++)
{
uint32 subresource = arraySlice * srcTexture->MipLevels() + mipLevel;
CopyTexture(dstTexture, subresource, 0, 0, 0, srcTexture, subresource);
}
}
}
else if (srcTexture)
{
@@ -751,7 +792,7 @@ void GPUContextWebGPU::OnDrawCall()
}
// Check if need to start a new render pass
if (_renderPassDirty)
if (_renderPassDirty || !_renderPass)
{
FlushRenderPass();
}
@@ -815,13 +856,14 @@ void GPUContextWebGPU::FlushRenderPass()
// Start a new render pass
WGPURenderPassColorAttachment colorAttachments[GPU_MAX_RT_BINDED];
WGPURenderPassDepthStencilAttachment depthStencilAttachment = WGPU_RENDER_PASS_DEPTH_STENCIL_ATTACHMENT_INIT;
WGPURenderPassDepthStencilAttachment depthStencilAttachment;
WGPURenderPassDescriptor renderPassDesc = WGPU_RENDER_PASS_DESCRIPTOR_INIT;
renderPassDesc.colorAttachmentCount = _renderTargetCount;
renderPassDesc.colorAttachments = colorAttachments;
PendingClear clear;
_pipelineKey.MultiSampleCount = 1;
_pipelineKey.RenderTargetCount = _renderTargetCount;
GPUTextureViewSizeWebGPU attachmentSize;
for (int32 i = 0; i < renderPassDesc.colorAttachmentCount; i++)
{
auto& colorAttachment = colorAttachments[i];
@@ -838,43 +880,54 @@ void GPUContextWebGPU::FlushRenderPass()
}
_pipelineKey.MultiSampleCount = (int32)renderTarget->GetMSAA();
_pipelineKey.RenderTargetFormats[i] = renderTarget->Format;
attachmentSize.Set(renderTarget->RenderSize);
}
if (_depthStencil)
{
auto renderTarget = _depthStencil;
renderPassDesc.depthStencilAttachment = &depthStencilAttachment;
depthStencilAttachment.view = _depthStencil->ViewRender;
depthStencilAttachment.depthLoadOp = WGPULoadOp_Load;
depthStencilAttachment.depthStoreOp = _depthStencil->ReadOnly ? WGPUStoreOp_Discard : WGPUStoreOp_Store;
depthStencilAttachment.depthReadOnly = _depthStencil->ReadOnly;
if (_depthStencil->HasStencil)
depthStencilAttachment = WGPU_RENDER_PASS_DEPTH_STENCIL_ATTACHMENT_INIT;
depthStencilAttachment.view = renderTarget->ViewRender;
depthStencilAttachment.depthLoadOp = renderTarget->ReadOnly ? WGPULoadOp_Undefined : WGPULoadOp_Load;
depthStencilAttachment.depthStoreOp = renderTarget->ReadOnly ? WGPUStoreOp_Undefined : WGPUStoreOp_Store;
depthStencilAttachment.depthReadOnly = renderTarget->ReadOnly;
if (renderTarget->HasStencil)
{
depthStencilAttachment.stencilLoadOp = WGPULoadOp_Load;
depthStencilAttachment.stencilStoreOp = _depthStencil->ReadOnly ? WGPUStoreOp_Discard : WGPUStoreOp_Store;
depthStencilAttachment.depthReadOnly = _depthStencil->ReadOnly;
depthStencilAttachment.stencilLoadOp = renderTarget->ReadOnly ? WGPULoadOp_Undefined : WGPULoadOp_Load;
depthStencilAttachment.stencilStoreOp = renderTarget->ReadOnly ? WGPUStoreOp_Undefined : WGPUStoreOp_Store;
depthStencilAttachment.depthReadOnly = renderTarget->ReadOnly;
depthStencilAttachment.stencilReadOnly = renderTarget->ReadOnly;
}
else
{
depthStencilAttachment.stencilClearValue = 0;
depthStencilAttachment.stencilLoadOp = WGPULoadOp_Clear;
depthStencilAttachment.stencilStoreOp = WGPUStoreOp_Discard;
depthStencilAttachment.stencilLoadOp = WGPULoadOp_Undefined;
depthStencilAttachment.stencilStoreOp = WGPUStoreOp_Undefined;
depthStencilAttachment.stencilReadOnly = true;
}
if (FindClear(_depthStencil, clear))
if (!renderTarget->ReadOnly && FindClear(renderTarget, clear))
{
depthStencilAttachment.depthLoadOp = WGPULoadOp_Clear;
depthStencilAttachment.depthClearValue = clear.Depth;
if (_depthStencil->HasStencil)
if (renderTarget->HasStencil)
{
depthStencilAttachment.stencilLoadOp = WGPULoadOp_Clear;
depthStencilAttachment.stencilClearValue = clear.Stencil;
}
}
_pipelineKey.DepthStencilFormat = _depthStencil->Format;
else
{
depthStencilAttachment.depthClearValue = 0.0f;
depthStencilAttachment.stencilClearValue = 0;
}
_pipelineKey.DepthStencilFormat = renderTarget->Format;
attachmentSize.Set(renderTarget->RenderSize);
}
else
{
_pipelineKey.DepthStencilFormat = WGPUTextureFormat_Undefined;
}
ASSERT(attachmentSize.Packed != 0);
_renderPass = wgpuCommandEncoderBeginRenderPass(Encoder, &renderPassDesc);
ASSERT(_renderPass);
@@ -885,11 +938,11 @@ void GPUContextWebGPU::FlushRenderPass()
if (_stencilRef != 0)
wgpuRenderPassEncoderSetStencilReference(_renderPass, _stencilRef);
auto scissorRect = _scissorRect;
// TODO: skip calling this if scissorRect is default (0, 0, attachment width, attachment height)
wgpuRenderPassEncoderSetScissorRect(_renderPass, (uint32_t)scissorRect.GetX(), (uint32_t)scissorRect.GetY(), (uint32_t)scissorRect.GetWidth(), (uint32_t)scissorRect.GetHeight());
if (scissorRect != Rectangle(0, 0, attachmentSize.Width, attachmentSize.Height))
wgpuRenderPassEncoderSetScissorRect(_renderPass, (uint32_t)scissorRect.GetX(), (uint32_t)scissorRect.GetY(), (uint32_t)scissorRect.GetWidth(), (uint32_t)scissorRect.GetHeight());
auto viewport = _viewport;
// TODO: skip calling this if viewport is default (0, 0, attachment width, attachment height, 0, 1)
wgpuRenderPassEncoderSetViewport(_renderPass, viewport.X, viewport.Y, viewport.Width, viewport.Height, viewport.MinDepth, viewport.MaxDepth);
if (viewport != Viewport(Float2(attachmentSize.Width, attachmentSize.Height)))
wgpuRenderPassEncoderSetViewport(_renderPass, viewport.X, viewport.Y, viewport.Width, viewport.Height, viewport.MinDepth, viewport.MaxDepth);
// Auto-dirty pipeline when new render pass starts
if (_pipelineState)
@@ -937,6 +990,7 @@ void GPUContextWebGPU::FlushBindGroup()
break;
}
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
{
ASSERT_LOW_LAYER(descriptor.BindingType == SpirvShaderResourceBindingType::SRV);
auto view = _shaderResources[descriptor.Slot];
@@ -952,7 +1006,19 @@ void GPUContextWebGPU::FlushBindGroup()
LOG(Error, "Missing resource {} at slot {} of binding space {}", (int32)descriptor.ResourceType, descriptor.Slot, (int32)descriptor.BindingType);
CRASH;
}
view = defaultTexture->View(0);
switch (descriptor.ResourceType)
{
case SpirvShaderResourceType::Texture3D:
view = defaultTexture->ViewVolume();
break;
case SpirvShaderResourceType::Texture1DArray:
case SpirvShaderResourceType::Texture2DArray:
view = defaultTexture->ViewArray();
break;
default:
view = defaultTexture->View(0);
break;
}
ptr = (GPUResourceViewPtrWebGPU*)view->GetNativePtr();
entry.textureView = ptr->TextureView->View;
}
@@ -976,6 +1042,7 @@ void GPUContextWebGPU::FlushBindGroup()
}
break;
}
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
{
GPUConstantBufferWebGPU* uniform = _constantBuffers[descriptor.Slot];
@@ -983,20 +1050,10 @@ void GPUContextWebGPU::FlushBindGroup()
{
entry.buffer = uniform->Allocation.Buffer;
entry.size = uniform->AllocationSize;
_dynamicOffsets.Add(uniform->Allocation.Offset);
}
else
CRASH; // TODO: add dummy buffer as fallback
break;
}
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
{
GPUConstantBufferWebGPU* uniform = _constantBuffers[descriptor.Slot];
if (uniform && uniform->Allocation.Buffer)
{
entry.buffer = uniform->Allocation.Buffer;
entry.offset = uniform->Allocation.Offset;
entry.size = uniform->AllocationSize;
if (descriptor.DescriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
entry.offset = uniform->Allocation.Offset;
else
_dynamicOffsets.Add(uniform->Allocation.Offset);
}
else
CRASH; // TODO: add dummy buffer as fallback
@@ -1015,9 +1072,21 @@ void GPUContextWebGPU::FlushBindGroup()
// Create a bind group
bindGroupDesc.entryCount = _bindGroupEntries.Count();
bindGroupDesc.entries = entriesPtr;
#if BUILD_DEBUG
for (int32 i = 0; i < bindGroupDesc.entryCount; i++)
{
auto& e = bindGroupDesc.entries[i];
if ((e.buffer != nullptr) + (e.sampler != nullptr) + (e.textureView != nullptr) != 1)
{
LOG(Error, "Invalid binding in group {} at index {} ({})", groupIndex, i, _pipelineState->GetName());
LOG(Error, " > sampler: {}", (uint32)e.sampler);
LOG(Error, " > textureView: {}", (uint32)e.textureView);
LOG(Error, " > buffer: {}", (uint32)e.buffer);
}
}
#endif
WGPUBindGroup bindGroup = wgpuDeviceCreateBindGroup(_device->Device, &bindGroupDesc);
_unusedBindGroups.Add(bindGroup);
// TODO: cache and release them
// Bind group
wgpuRenderPassEncoderSetBindGroup(_renderPass, groupIndex, bindGroup, _dynamicOffsets.Count(), _dynamicOffsets.Get());
@@ -28,22 +28,6 @@ GPUVertexLayoutWebGPU::GPUVertexLayoutWebGPU(GPUDeviceWebGPU* device, const Elem
: GPUResourceBase<GPUDeviceWebGPU, GPUVertexLayout>(device, StringView::Empty)
{
SetElements(elements, explicitOffsets);
Layout = WGPU_VERTEX_BUFFER_LAYOUT_INIT;
Layout.stepMode = WGPUVertexStepMode_Vertex;
Layout.arrayStride = GetStride();
Layout.attributeCount = elements.Count();
Layout.attributes = Attributes;
const VertexElement* srcElements = GetElements().Get();
for (int32 i = 0; i < elements.Count(); i++)
{
const VertexElement& src = srcElements[i];
WGPUVertexAttribute& dst = Attributes[i];
dst.nextInChain = nullptr;
dst.format = RenderToolsWebGPU::ToVertexFormat(src.Format);
dst.offset = src.Offset;
if (src.PerInstance)
Layout.stepMode = WGPUVertexStepMode_Instance;
}
}
GPUDataUploaderWebGPU::Allocation GPUDataUploaderWebGPU::Allocate(uint32 size, WGPUBufferUsage usage, uint32 alignment)
@@ -183,6 +167,7 @@ bool GPUDeviceWebGPU::Init()
if (wgpuAdapterGetLimits(Adapter->Adapter, &limits) == WGPUStatus_Success)
{
MinUniformBufferOffsetAlignment = limits.minUniformBufferOffsetAlignment;
Limits.HasDrawIndirect = true;
Limits.HasDepthAsSRV = true;
Limits.HasReadOnlyDepth = true;
Limits.HasDepthClip = features.Contains(WGPUFeatureName_DepthClipControl);
@@ -431,6 +416,9 @@ bool GPUDeviceWebGPU::Init()
{
LOG(Info, "WebGPU: {}", WEBGPU_TO_STR(message));
}
static int32 LogSpamLeft = 20;
if (LogSpamLeft-- < 0)
CRASH; // Too many errors
#endif
};
@@ -11,6 +11,8 @@
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Graphics/PixelFormatExtensions.h"
#define WEBGPU_LOG_PSO 0
WGPUCompareFunction ToCompareFunction(ComparisonFunc value)
{
switch (value)
@@ -158,10 +160,9 @@ void GPUPipelineStateWebGPU::OnReleaseGPU()
uint32 GetHash(const GPUPipelineStateWebGPU::Key& key)
{
static_assert(sizeof(GPUPipelineStateWebGPU::Key) == sizeof(uint64) * 3, "Invalid PSO key size.");
static_assert(sizeof(GPUPipelineStateWebGPU::Key) == sizeof(uint64) * 2, "Invalid PSO key size.");
uint32 hash = GetHash(key.Packed[0]);
CombineHash(hash, GetHash(key.Packed[1]));
CombineHash(hash, GetHash(key.Packed[2]));
return hash;
}
@@ -175,6 +176,9 @@ WGPURenderPipeline GPUPipelineStateWebGPU::GetPipeline(const Key& key, GPUResour
#if GPU_ENABLE_RESOURCE_NAMING
ZoneText(_debugName.Get(), _debugName.Count() - 1);
#endif
#if WEBGPU_LOG_PSO
LOG(Info, "[WebGPU] GetPipeline: '{}'", String(_debugName.Get(), _debugName.Count() - 1));
#endif
// Lazy-init layout (cannot do it during Init as texture samplers that access eg. depth need to explicitly use UnfilterableFloat)
if (!PipelineDesc.layout)
@@ -182,10 +186,14 @@ WGPURenderPipeline GPUPipelineStateWebGPU::GetPipeline(const Key& key, GPUResour
// Build final pipeline description
_depthStencilDesc.format = (WGPUTextureFormat)key.DepthStencilFormat;
PipelineDesc.depthStencil = key.DepthStencilFormat ? &_depthStencilDesc : nullptr; // Unbind depth stencil state when no debug buffer is bound
PipelineDesc.multisample.count = key.MultiSampleCount;
_fragmentDesc.targetCount = key.RenderTargetCount;
for (int32 i = 0; i < _fragmentDesc.targetCount; i++)
_colorTargets[i].format = (WGPUTextureFormat)key.RenderTargetFormats[i];
if (PS)
{
_fragmentDesc.targetCount = key.RenderTargetCount;
for (int32 i = 0; i < _fragmentDesc.targetCount; i++)
_colorTargets[i].format = (WGPUTextureFormat)key.RenderTargetFormats[i];
}
WGPUVertexBufferLayout buffers[GPU_MAX_VB_BINDED];
if (key.VertexLayout)
{
@@ -253,6 +261,10 @@ WGPURenderPipeline GPUPipelineStateWebGPU::GetPipeline(const Key& key, GPUResour
void GPUPipelineStateWebGPU::InitLayout(GPUResourceView* shaderResources[GPU_MAX_SR_BINDED])
{
#if WEBGPU_LOG_PSO
// Debug log for PSOs with specific name
const bool log = true;// StringAnsiView(_debugName.Get(), _debugName.Count() - 1).Contains("PS_HalfDepth");
#endif
// Count the biggest bind group entries (for all shaders) to allocate reused memory
int32 maxEntriesCount = 0;
@@ -276,6 +288,11 @@ void GPUPipelineStateWebGPU::InitLayout(GPUResourceView* shaderResources[GPU_MAX
int32 entriesCount = descriptors->DescriptorTypesCount;
Platform::MemoryClear(entries.Get(), sizeof(WGPUBindGroupLayoutEntry) * entriesCount);
auto visibility = groupIndex == 0 ? WGPUShaderStage_Vertex : WGPUShaderStage_Fragment;
#if WEBGPU_LOG_PSO
if (log)
LOG(Info, " > group {} - {}", groupIndex, groupIndex == 0 ? TEXT("Vertex") : TEXT("Fragment"));
const Char* samplerType = TEXT("?");
#endif
for (int32 index = 0; index < entriesCount; index++)
{
auto& descriptor = descriptors->DescriptorTypes[index];
@@ -287,16 +304,87 @@ void GPUPipelineStateWebGPU::InitLayout(GPUResourceView* shaderResources[GPU_MAX
{
case VK_DESCRIPTOR_TYPE_SAMPLER:
entry.sampler.type = WGPUSamplerBindingType_Undefined;
if (descriptor.Slot == 4 || descriptor.Slot == 5) // Hack for ShadowSampler and ShadowSamplerLinear (this could get binded samplers table just like for shaderResources)
entry.sampler.type = WGPUSamplerBindingType_Comparison;
#if WEBGPU_LOG_PSO
switch (entry.sampler.type)
{
case WGPUSamplerBindingType_BindingNotUsed:
samplerType = TEXT("BindingNotUsed");
break;
case WGPUSamplerBindingType_Undefined:
samplerType = TEXT("Undefined");
break;
case WGPUSamplerBindingType_Filtering:
samplerType = TEXT("Filtering");
break;
case WGPUSamplerBindingType_NonFiltering:
samplerType = TEXT("NonFiltering");
break;
case WGPUSamplerBindingType_Comparison:
samplerType = TEXT("Comparison");
break;
}
if (log)
LOG(Info, " > [{}] sampler ({})", entry.binding, samplerType);
#endif
break;
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
entry.texture.sampleType = WGPUTextureSampleType_Undefined;
if (shaderResources[descriptor.Slot])
{
// Hack to use the sample type directly from the view which allows to fix incorrect Depth Buffer reading that allows only manual Load when UnfilterableFloat is used (see SAMPLE_RT_LOAD)
// Hack to use the sample type directly from the view which allows to fix incorrect Depth Buffer reading that allows only manual Load when UnfilterableFloat is used (see SAMPLE_RT_DEPTH)
auto ptr = (GPUResourceViewPtrWebGPU*)shaderResources[descriptor.Slot]->GetNativePtr();
if (ptr && ptr->TextureView)
entry.texture.sampleType = ptr->TextureView->SampleType;
}
#if WEBGPU_LOG_PSO
if (log)
{
switch (entry.texture.sampleType)
{
case WGPUTextureSampleType_BindingNotUsed:
samplerType = TEXT("BindingNotUsed");
break;
case WGPUTextureSampleType_Undefined:
samplerType = TEXT("Undefined");
break;
case WGPUTextureSampleType_Float:
samplerType = TEXT("Float");
break;
case WGPUTextureSampleType_UnfilterableFloat:
samplerType = TEXT("UnfilterableFloat");
break;
case WGPUTextureSampleType_Depth:
samplerType = TEXT("Depth");
break;
case WGPUTextureSampleType_Sint:
samplerType = TEXT("Sint");
break;
case WGPUTextureSampleType_Uint:
samplerType = TEXT("Uint");
break;
}
switch (descriptor.ResourceType)
{
case SpirvShaderResourceType::Texture1D:
LOG(Info, " > [{}] texture 1D ({})", entry.binding, samplerType);
break;
case SpirvShaderResourceType::Texture2D:
LOG(Info, " > [{}] texture 2D ({})", entry.binding, samplerType);
break;
case SpirvShaderResourceType::Texture3D:
LOG(Info, " > [{}] texture 3D ({})", entry.binding, samplerType);
break;
case SpirvShaderResourceType::TextureCube:
LOG(Info, " > [{}] texture Cube ({})", entry.binding, samplerType);
break;
case SpirvShaderResourceType::Texture2DArray:
LOG(Info, " > [{}] texture 2D array ({})", entry.binding, samplerType);
break;
}
}
#endif
switch (descriptor.ResourceType)
{
case SpirvShaderResourceType::Texture1D:
@@ -326,11 +414,19 @@ void GPUPipelineStateWebGPU::InitLayout(GPUResourceView* shaderResources[GPU_MAX
entry.buffer.type = WGPUBufferBindingType_ReadOnlyStorage;
else
entry.buffer.type = WGPUBufferBindingType_Storage;
#if WEBGPU_LOG_PSO
if (log)
LOG(Info, " > [{}] storage buffer (read-only = {}, dynamic = {})", entry.binding, entry.buffer.type == WGPUBufferBindingType_ReadOnlyStorage, entry.buffer.hasDynamicOffset);
#endif
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
entry.buffer.hasDynamicOffset = true;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
entry.buffer.type = WGPUBufferBindingType_Uniform;
#if WEBGPU_LOG_PSO
if (log)
LOG(Info, " > [{}] uniform buffer (dynamic = {})", entry.binding, entry.buffer.hasDynamicOffset);
#endif
break;
default:
#if GPU_ENABLE_DIAGNOSTICS
@@ -414,33 +510,33 @@ bool GPUPipelineStateWebGPU::Init(const Description& desc)
}
}
PipelineDesc.multisample.alphaToCoverageEnabled = desc.BlendMode.AlphaToCoverageEnable;
PipelineDesc.fragment = &_fragmentDesc;
_fragmentDesc = WGPU_FRAGMENT_STATE_INIT;
_fragmentDesc.targets = _colorTargets;
Platform::MemoryClear(&_colorTargets, sizeof(_colorTargets));
if (desc.BlendMode.BlendEnable)
{
_blendState = WGPU_BLEND_STATE_INIT;
_blendState.color = ToBlendComponent(desc.BlendMode.BlendOp, desc.BlendMode.SrcBlend, desc.BlendMode.DestBlend);
_blendState.alpha = ToBlendComponent(desc.BlendMode.BlendOpAlpha, desc.BlendMode.SrcBlendAlpha, desc.BlendMode.DestBlendAlpha);
for (auto& e : _colorTargets)
e.blend = &_blendState;
}
WGPUColorWriteMask writeMask = WGPUColorWriteMask_All;
if (desc.BlendMode.RenderTargetWriteMask != BlendingMode::ColorWrite::All)
{
writeMask = 0;
if (EnumHasAllFlags(desc.BlendMode.RenderTargetWriteMask, BlendingMode::ColorWrite::Red))
writeMask |= WGPUColorWriteMask_Red;
if (EnumHasAllFlags(desc.BlendMode.RenderTargetWriteMask, BlendingMode::ColorWrite::Green))
writeMask |= WGPUColorWriteMask_Green;
if (EnumHasAllFlags(desc.BlendMode.RenderTargetWriteMask, BlendingMode::ColorWrite::Blue))
writeMask |= WGPUColorWriteMask_Blue;
if (EnumHasAllFlags(desc.BlendMode.RenderTargetWriteMask, BlendingMode::ColorWrite::Alpha))
writeMask |= WGPUColorWriteMask_Alpha;
}
if (desc.PS)
{
PipelineDesc.fragment = &_fragmentDesc;
_fragmentDesc = WGPU_FRAGMENT_STATE_INIT;
_fragmentDesc.targets = _colorTargets;
Platform::MemoryClear(&_colorTargets, sizeof(_colorTargets));
if (desc.BlendMode.BlendEnable)
{
_blendState = WGPU_BLEND_STATE_INIT;
_blendState.color = ToBlendComponent(desc.BlendMode.BlendOp, desc.BlendMode.SrcBlend, desc.BlendMode.DestBlend);
_blendState.alpha = ToBlendComponent(desc.BlendMode.BlendOpAlpha, desc.BlendMode.SrcBlendAlpha, desc.BlendMode.DestBlendAlpha);
for (auto& e : _colorTargets)
e.blend = &_blendState;
}
WGPUColorWriteMask writeMask = WGPUColorWriteMask_All;
if (desc.BlendMode.RenderTargetWriteMask != BlendingMode::ColorWrite::All)
{
writeMask = 0;
if (EnumHasAllFlags(desc.BlendMode.RenderTargetWriteMask, BlendingMode::ColorWrite::Red))
writeMask |= WGPUColorWriteMask_Red;
if (EnumHasAllFlags(desc.BlendMode.RenderTargetWriteMask, BlendingMode::ColorWrite::Green))
writeMask |= WGPUColorWriteMask_Green;
if (EnumHasAllFlags(desc.BlendMode.RenderTargetWriteMask, BlendingMode::ColorWrite::Blue))
writeMask |= WGPUColorWriteMask_Blue;
if (EnumHasAllFlags(desc.BlendMode.RenderTargetWriteMask, BlendingMode::ColorWrite::Alpha))
writeMask |= WGPUColorWriteMask_Alpha;
}
uint16 outputsCount = desc.PS->GetBindings().OutputsCount;
for (uint16 rtIndex = 0; rtIndex < outputsCount; rtIndex++)
_colorTargets[rtIndex].writeMask = writeMask;
@@ -28,7 +28,7 @@ public:
uint8 RenderTargetFormats[GPU_MAX_RT_BINDED];
class GPUVertexLayoutWebGPU* VertexLayout;
};
uint64 Packed[3];
uint64 Packed[2];
};
FORCE_INLINE bool operator==(const Key& other) const
@@ -41,19 +41,19 @@ bool GPUSamplerWebGPU::OnInit()
switch (_desc.Filter)
{
case GPUSamplerFilter::Point:
samplerDesc.magFilter = samplerDesc.magFilter = WGPUFilterMode_Nearest;
samplerDesc.magFilter = samplerDesc.minFilter = WGPUFilterMode_Nearest;
samplerDesc.mipmapFilter = WGPUMipmapFilterMode_Nearest;
break;
case GPUSamplerFilter::Bilinear:
samplerDesc.magFilter = samplerDesc.magFilter = WGPUFilterMode_Linear;
samplerDesc.magFilter = samplerDesc.minFilter = WGPUFilterMode_Linear;
samplerDesc.mipmapFilter = WGPUMipmapFilterMode_Nearest;
break;
case GPUSamplerFilter::Trilinear:
samplerDesc.magFilter = samplerDesc.magFilter = WGPUFilterMode_Linear;
samplerDesc.magFilter = samplerDesc.minFilter = WGPUFilterMode_Linear;
samplerDesc.mipmapFilter = WGPUMipmapFilterMode_Linear;
break;
case GPUSamplerFilter::Anisotropic:
samplerDesc.magFilter = samplerDesc.magFilter = WGPUFilterMode_Linear;
samplerDesc.magFilter = samplerDesc.minFilter = WGPUFilterMode_Linear;
samplerDesc.mipmapFilter = WGPUMipmapFilterMode_Linear;
break;
}
@@ -70,7 +70,7 @@ GPUTextureView* GPUSwapChainWebGPU::GetBackBufferView()
viewDesc.arrayLayerCount = 1;
viewDesc.aspect = WGPUTextureAspect_All;
viewDesc.usage = wgpuTextureGetUsage(surfaceTexture.texture);
_surfaceView.Create(surfaceTexture.texture, &viewDesc);
_surfaceView.Create(surfaceTexture.texture, viewDesc);
}
return &_surfaceView;
}
@@ -32,12 +32,38 @@ WGPUTextureFormat DropStencil(WGPUTextureFormat format)
}
}
void GPUTextureViewWebGPU::Create(WGPUTexture texture, WGPUTextureViewDescriptor const* desc)
void SetWebGPUTextureViewSampler(GPUTextureView* view, uint32 samplerType)
{
((GPUTextureViewWebGPU*)view)->SampleType = (WGPUTextureSampleType)samplerType;
}
void GPUTextureViewWebGPU::Create(WGPUTexture texture, const WGPUTextureViewDescriptor& desc)
{
if (View)
wgpuTextureViewRelease(View);
Texture = texture;
View = wgpuTextureCreateView(texture, desc);
auto viewDesc = desc;
auto renderDesc = desc;
auto separateViews = false;
// Render views cannot have more than 1 mip levels count
if (desc.usage & WGPUTextureUsage_RenderAttachment && renderDesc.mipLevelCount > 1)
{
renderDesc.mipLevelCount = 1;
separateViews = true;
}
// Depth-stencil textures expose depth-only when binding to shaders (unless via custom _handleStencil view) so make separate ViewRender for rendering with all components
if (desc.aspect == WGPUTextureAspect_All && ::HasStencil(desc.format))
{
viewDesc.aspect = WGPUTextureAspect_DepthOnly;
viewDesc.format = DropStencil(viewDesc.format);
separateViews = true;
}
// Create views
View = wgpuTextureCreateView(texture, &viewDesc);
if (!View)
{
#if GPU_ENABLE_RESOURCE_NAMING
@@ -46,18 +72,13 @@ void GPUTextureViewWebGPU::Create(WGPUTexture texture, WGPUTextureViewDescriptor
LOG(Error, "Failed to create a view for texture");
#endif
}
ViewRender = View;
if (separateViews)
ViewRender = wgpuTextureCreateView(texture, &renderDesc);
else
ViewRender = View;
// Depth-stencil textures expose depth-only when binding to shaders (unless via custom _handleStencil view) so make separate ViewRender for rendering with all components
if (desc && desc->aspect == WGPUTextureAspect_All && ::HasStencil(desc->format))
{
auto depthOnlyDesc = *desc;
depthOnlyDesc.aspect = WGPUTextureAspect_DepthOnly;
depthOnlyDesc.format = DropStencil(depthOnlyDesc.format);
View = wgpuTextureCreateView(texture, &depthOnlyDesc);
}
Format = desc ? desc->format : wgpuTextureGetFormat(texture);
// Cache metadata
Format = desc.format;
switch (Format)
{
case WGPUTextureFormat_Depth16Unorm:
@@ -76,6 +97,8 @@ void GPUTextureViewWebGPU::Create(WGPUTexture texture, WGPUTextureViewDescriptor
SampleType = WGPUTextureSampleType_Undefined;
break;
}
RenderSize.Width = Math::Max<uint16>(wgpuTextureGetWidth(Texture) >> renderDesc.baseMipLevel, 1);
RenderSize.Height = Math::Max<uint16>(wgpuTextureGetHeight(Texture) >> renderDesc.baseMipLevel, 1);
}
void GPUTextureViewWebGPU::Release()
@@ -113,21 +136,22 @@ bool GPUTextureWebGPU::OnInit()
textureDesc.usage |= WGPUTextureUsage_RenderAttachment;
textureDesc.size.width = _desc.Width;
textureDesc.size.height = _desc.Height;
textureDesc.size.depthOrArrayLayers = _desc.Depth;
switch (_desc.Dimensions)
{
case TextureDimensions::Texture:
_viewDimension = IsArray() ? WGPUTextureViewDimension_2DArray : WGPUTextureViewDimension_2D;
textureDesc.dimension = WGPUTextureDimension_2D;
textureDesc.size.depthOrArrayLayers = _desc.ArraySize;
break;
case TextureDimensions::VolumeTexture:
_viewDimension = WGPUTextureViewDimension_3D;
textureDesc.dimension = WGPUTextureDimension_3D;
textureDesc.size.depthOrArrayLayers = _desc.Depth;
break;
case TextureDimensions::CubeTexture:
_viewDimension = _desc.ArraySize > 6 ? WGPUTextureViewDimension_CubeArray : WGPUTextureViewDimension_Cube;
textureDesc.dimension = WGPUTextureDimension_2D;
textureDesc.size.depthOrArrayLayers *= 6; // Each cubemap uses 6 array slices
textureDesc.size.depthOrArrayLayers = _desc.ArraySize;
break;
}
textureDesc.format = RenderToolsWebGPU::ToTextureFormat(Format());
@@ -136,7 +160,7 @@ bool GPUTextureWebGPU::OnInit()
textureDesc.viewFormats = &textureDesc.format;
textureDesc.viewFormatCount = 1;
_format = textureDesc.format;
_usage = textureDesc.usage;
Usage = textureDesc.usage;
Texture = wgpuDeviceCreateTexture(_device->Device, &textureDesc);
if (!Texture)
return true;
@@ -179,7 +203,7 @@ void GPUTextureWebGPU::OnResidentMipsChanged()
// Update the view to handle base mip level as highest resident mip
WGPUTextureViewDescriptor viewDesc = WGPU_TEXTURE_VIEW_DESCRIPTOR_INIT;
viewDesc.format = _format;
viewDesc.usage = _usage;
viewDesc.usage = Usage;
viewDesc.dimension = _viewDimension;
viewDesc.baseMipLevel = MipLevels() - ResidentMipLevels();
viewDesc.mipLevelCount = ResidentMipLevels();
@@ -188,7 +212,7 @@ void GPUTextureWebGPU::OnResidentMipsChanged()
GPUTextureViewWebGPU& view = IsVolume() ? _handleVolume : _handlesPerSlice[0];
if (view.GetParent() == nullptr)
view.Init(this, _desc.Format, _desc.MultiSampleLevel);
view.Create(Texture, &viewDesc);
view.Create(Texture, viewDesc);
}
void GPUTextureWebGPU::OnReleaseGPU()
@@ -220,7 +244,7 @@ void GPUTextureWebGPU::InitHandles()
viewDesc.label = { _name.Get(), (size_t)_name.Length() };
#endif
viewDesc.format = _format;
viewDesc.usage = _usage;
viewDesc.usage = Usage;
viewDesc.dimension = _viewDimension;
viewDesc.mipLevelCount = MipLevels();
viewDesc.arrayLayerCount = ArraySize();
@@ -235,7 +259,7 @@ void GPUTextureWebGPU::InitHandles()
{
auto& view = _handleVolume;
view.Init(this, format, msaa);
view.Create(Texture, &viewDesc);
view.Create(Texture, viewDesc);
}
// Init per slice views
@@ -249,7 +273,7 @@ void GPUTextureWebGPU::InitHandles()
//viewDesc.arrayLayerCount = 1;
auto& view = _handlesPerSlice[sliceIndex];
view.Init(this, format, msaa);
view.Create(Texture, &viewDesc);
view.Create(Texture, viewDesc);
view.DepthSlice = sliceIndex;
}
}
@@ -263,7 +287,7 @@ void GPUTextureWebGPU::InitHandles()
{
auto& view = _handleArray;
view.Init(this, format, msaa);
view.Create(Texture, &viewDesc);
view.Create(Texture, viewDesc);
}
// Create per array slice handles
@@ -275,7 +299,7 @@ void GPUTextureWebGPU::InitHandles()
viewDesc.arrayLayerCount = 1;
auto& view = _handlesPerSlice[arrayIndex];
view.Init(this, format, msaa);
view.Create(Texture, &viewDesc);
view.Create(Texture, viewDesc);
}
viewDesc.baseArrayLayer = 0;
viewDesc.arrayLayerCount = MipLevels();
@@ -287,7 +311,7 @@ void GPUTextureWebGPU::InitHandles()
_handlesPerSlice.Resize(1, false);
auto& view = _handlesPerSlice[0];
view.Init(this, format, msaa);
view.Create(Texture, &viewDesc);
view.Create(Texture, viewDesc);
}
// Init per mip map handles
@@ -308,7 +332,7 @@ void GPUTextureWebGPU::InitHandles()
auto& view = slice[mipIndex];
viewDesc.baseMipLevel = mipIndex;
view.Init(this, format, msaa);
view.Create(Texture, &viewDesc);
view.Create(Texture, viewDesc);
}
}
viewDesc.dimension = _viewDimension;
@@ -319,7 +343,7 @@ void GPUTextureWebGPU::InitHandles()
{
auto& view = _handleReadOnlyDepth;
view.Init(this, format, msaa);
view.Create(Texture, &viewDesc);
view.Create(Texture, viewDesc);
view.ReadOnly = true;
}
@@ -339,7 +363,7 @@ void GPUTextureWebGPU::InitHandles()
viewDesc.aspect = WGPUTextureAspect_StencilOnly;
viewDesc.format = WGPUTextureFormat_Stencil8;
_handleStencil.Init(this, stencilFormat, msaa);
_handleStencil.Create(Texture, &viewDesc);
_handleStencil.Create(Texture, viewDesc);
}
}
@@ -9,6 +9,28 @@
#if GRAPHICS_API_WEBGPU
struct GPUTextureViewSizeWebGPU
{
union
{
struct
{
uint16 Width, Height;
};
uint32 Packed = 0;
};
FORCE_INLINE void Set(GPUTextureViewSizeWebGPU other)
{
if (Packed == 0)
Packed = other.Packed;
else
{
ASSERT(Packed == other.Packed);
}
}
};
/// <summary>
/// The texture view for Web GPU backend.
/// </summary>
@@ -36,13 +58,14 @@ public:
bool HasStencil = false;
bool ReadOnly = false;
uint32 DepthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
GPUTextureViewSizeWebGPU RenderSize;
WGPUTextureFormat Format = WGPUTextureFormat_Undefined;
WGPUTextureSampleType SampleType = WGPUTextureSampleType_Undefined;
GPUResourceViewPtrWebGPU Ptr;
public:
using GPUTextureView::Init;
void Create(WGPUTexture texture, WGPUTextureViewDescriptor const* desc = nullptr);
void Create(WGPUTexture texture, const WGPUTextureViewDescriptor& desc);
void Release();
public:
@@ -70,7 +93,6 @@ private:
#endif
WGPUTextureFormat _format = WGPUTextureFormat_Undefined;
WGPUTextureViewDimension _viewDimension = WGPUTextureViewDimension_Undefined;
WGPUTextureUsage _usage = 0;
public:
GPUTextureWebGPU(GPUDeviceWebGPU* device, const StringView& name)
@@ -81,6 +103,8 @@ public:
public:
// Handle to the WebGPU texture object.
WGPUTexture Texture = nullptr;
// Usage flags fo the created texture.
WGPUTextureUsage Usage = 0;
public:
// [GPUTexture]
@@ -14,10 +14,6 @@ class GPUVertexLayoutWebGPU : public GPUResourceBase<GPUDeviceWebGPU, GPUVertexL
{
public:
GPUVertexLayoutWebGPU(GPUDeviceWebGPU* device, const Elements& elements, bool explicitOffsets);
public:
WGPUVertexBufferLayout Layout;
WGPUVertexAttribute Attributes[GPU_MAX_VS_ELEMENTS];
};
#endif
@@ -50,12 +50,7 @@ bool ParticleEmitterGPUGenerator::loadTexture(Node* caller, Box* box, const Seri
bool ParticleEmitterGPUGenerator::sampleSceneTexture(Node* caller, Box* box, const SerializedMaterialParam& texture, Value& result)
{
ASSERT(caller && box && texture.ID.IsValid());
// Cache data
auto parent = box->GetParent<Node>();
const bool isCubemap = texture.Type == MaterialParameterType::CubeTexture;
const bool isVolume = texture.Type == MaterialParameterType::GPUTextureVolume;
const bool isArray = texture.Type == MaterialParameterType::GPUTextureArray;
// Check if has variable assigned and it's a valid type
if (texture.Type != MaterialParameterType::Texture
@@ -121,6 +121,7 @@ bool AmbientOcclusionPass::setupResources()
// Create pipeline states
auto psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle;
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::Red;
if (!_psPrepareDepths->IsValid())
{
psDesc.PS = shader->GetPS("PS_PrepareDepths");
@@ -144,6 +145,7 @@ bool AmbientOcclusionPass::setupResources()
return true;
}
}
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::RG;
for (int32 i = 0; i < ARRAY_COUNT(_psGenerate); i++)
{
if (!_psGenerate[i]->IsValid())
@@ -174,7 +176,7 @@ bool AmbientOcclusionPass::setupResources()
return true;
}
psDesc.BlendMode = BlendingMode::Multiply;
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::Alpha;
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::Alpha; // Write only into AO channel in GBuffer
if (_depthBounds)
{
psDesc.DepthEnable = psDesc.DepthBoundsEnable = true;
@@ -278,7 +280,7 @@ void AmbientOcclusionPass::Render(RenderContext& renderContext)
GPUTextureView* depthBufferApply = _depthBounds ? renderContext.Buffers->DepthBuffer->ViewReadOnlyDepth() : nullptr;
// Request temporary buffers
GPUTexture* m_halfDepths[4];
GPUTexture* m_halfDepths[4] = {};
GPUTexture* m_pingPongHalfResultA;
GPUTexture* m_pingPongHalfResultB;
GPUTexture* m_finalResults;
@@ -286,6 +288,8 @@ void AmbientOcclusionPass::Render(RenderContext& renderContext)
GPUTextureDescription tempDesc;
for (int i = 0; i < 4; i++)
{
if (settings.SkipHalfPixels && (i == 1 || i == 2))
continue;
#if SSAO_DEPTH_MIPS_ENABLE_AT_QUALITY_PRESET < 99
tempDesc = GPUTextureDescription::New2D(m_halfSizeX, m_halfSizeY, 0, SSAO_DEPTH_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget | GPUTextureFlags::PerMipViews);
#else
@@ -407,7 +411,7 @@ void AmbientOcclusionPass::Render(RenderContext& renderContext)
}
// Only do mipmaps for higher quality levels (not beneficial on quality level 1, and detrimental on quality level 0)
if (settings.QualityLevel > 1 && SSAO_DEPTH_MIPS_ENABLE_AT_QUALITY_PRESET < 99)
if (settings.QualityLevel > 1 && SSAO_DEPTH_MIPS_ENABLE_AT_QUALITY_PRESET < 99 && !settings.SkipHalfPixels)
{
for (int i = 1; i < SSAO_DEPTH_MIP_LEVELS; i++)
{
+4
View File
@@ -395,6 +395,7 @@ void RenderList::RunPostFxPass(GPUContext* context, RenderContext& renderContext
auto material = Settings.PostFxMaterials.Materials[i].Get();
if (material && material->IsReady() && material->IsPostFx() && material->GetInfo().PostFxLocation == locationA)
{
context->ResetSR();
ASSERT(needTempTarget);
context->SetRenderTarget(*output);
bindParams.Input = *input;
@@ -431,6 +432,7 @@ void RenderList::RunPostFxPass(GPUContext* context, RenderContext& renderContext
if (needTempTarget)
RenderTargetPool::Release(output);
context->ResetSR();
}
void RenderList::RunMaterialPostFxPass(GPUContext* context, RenderContext& renderContext, MaterialPostFxLocation location, GPUTexture*& input, GPUTexture*& output)
@@ -441,6 +443,7 @@ void RenderList::RunMaterialPostFxPass(GPUContext* context, RenderContext& rende
auto material = Settings.PostFxMaterials.Materials[i].Get();
if (material && material->IsReady() && material->IsPostFx() && material->GetInfo().PostFxLocation == location)
{
context->ResetSR();
context->SetRenderTarget(*output);
bindParams.Input = *input;
material->Bind(bindParams);
@@ -459,6 +462,7 @@ void RenderList::RunCustomPostFxPass(GPUContext* context, RenderContext& renderC
{
if (fx->Location == location)
{
context->ResetSR();
if (fx->UseSingleTarget || output == nullptr)
{
fx->Render(context, renderContext, input, nullptr);
+5
View File
@@ -1178,6 +1178,11 @@ void ShadowsPass::SetupShadows(RenderContext& renderContext, RenderContextBatch&
LOG(Fatal, "Failed to setup shadow map of size {0}x{1} and format {2}", desc.Width, desc.Height, ScriptingEnum::ToString(desc.Format));
return;
}
#if PLATFORM_WEB
// Hack to fix WebGPU limitation that requires to specify different sampler type manually to sample depth texture
void SetWebGPUTextureViewSampler(GPUTextureView * view, uint32 samplerType);
SetWebGPUTextureViewSampler(shadows.ShadowMapAtlas->View(), 0x00000004); // WGPUTextureSampleType_Depth
#endif
shadows.ClearShadowMapAtlas = true;
shadows.Resolution = atlasResolution;
shadows.ViewOrigin = renderContext.View.Origin;
+11 -4
View File
@@ -68,13 +68,14 @@ bool MultiScaler::setupResources()
}
if (!_psHalfDepth.IsValid())
{
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::Red;
psDesc.PS = shader->GetPS("PS_HalfDepth", 0);
if (_psHalfDepth[0]->Init(psDesc))
return true;
psDesc.PS = shader->GetPS("PS_HalfDepth", 2);
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::Red;
if (_psHalfDepth[2]->Init(psDesc))
return true;
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::None;
psDesc.PS = shader->GetPS("PS_HalfDepth", 1);
psDesc.DepthWriteEnable = true;
psDesc.DepthEnable = true;
@@ -214,9 +215,13 @@ void MultiScaler::Filter(FilterMode mode, GPUContext* context, int32 width, int3
void MultiScaler::DownscaleDepth(GPUContext* context, int32 dstWidth, int32 dstHeight, GPUTexture* src, GPUTextureView* dst)
{
PROFILE_GPU_CPU("Downscale Depth");
bool outputDepth = ((GPUTexture*)dst->GetParent())->IsDepthStencil();
if (checkIfSkipPass())
{
context->ClearDepth(dst);
if (outputDepth)
context->ClearDepth(dst);
else
context->Clear(dst, Color::Transparent);
return;
}
@@ -224,14 +229,16 @@ void MultiScaler::DownscaleDepth(GPUContext* context, int32 dstWidth, int32 dstH
Data data;
data.TexelSize.X = 1.0f / (float)src->Width();
data.TexelSize.Y = 1.0f / (float)src->Height();
bool outputDepth = ((GPUTexture*)dst->GetParent())->IsDepthStencil();
auto cb = _shader->GetShader()->GetCB(0);
context->UpdateCB(cb, &data);
context->BindCB(0, cb);
// Draw
context->SetViewportAndScissors((float)dstWidth, (float)dstHeight);
context->SetRenderTarget(dst, (GPUTextureView*)nullptr);
if (outputDepth)
context->SetRenderTarget(dst, (GPUTextureView*)nullptr);
else
context->SetRenderTarget(dst);
context->BindSR(0, src);
context->SetState(_psHalfDepth[outputDepth ? 1 : 0]);
context->DrawFullscreenTriangle();
@@ -27,8 +27,6 @@ namespace
MaterialValue* MaterialGenerator::sampleTextureRaw(Node* caller, Value& value, Box* box, SerializedMaterialParam* texture)
{
ASSERT(texture && box);
// Cache data
const auto parent = box->GetParent<ShaderGraphNode<>>();
const bool isCubemap = texture->Type == MaterialParameterType::CubeTexture;
const bool isArray = texture->Type == MaterialParameterType::GPUTextureArray;
@@ -101,7 +99,13 @@ MaterialValue* MaterialGenerator::sampleTextureRaw(Node* caller, Value& value, B
const Char* sampler = TEXT("SamplerLinearWrap");
// Sample texture
if (isNormalMap)
if (texture->AsInteger == (int32)MaterialSceneTextures::SceneDepth)
{
// Sample depth buffer
String sampledValue = String::Format(TEXT("SAMPLE_RT_DEPTH({0}, {1})"), texture->ShaderName, uv);
valueBox->Cache = writeLocal(VariantType::Float, sampledValue, parent);
}
else if (isNormalMap)
{
const Char* format = canUseSample ? TEXT("{0}.Sample({1}, {2}).xyz") : TEXT("{0}.SampleLevel({1}, {2}, {3}).xyz");
+11 -3
View File
@@ -74,12 +74,20 @@
#else
#define CAN_USE_TESSELLATION 0
#endif
#if defined(WGSL)
// Wrap not supported read-only Buffer binded as shader resource into StructuredBuffer to be used as storage on WebGPU
// Alias read-only Buffer binded as shader resource into StructuredBuffer to be used as storage on WebGPU (not supported)
#define CAN_USE_TYPED_BUFFER_LOADS 0
#define Buffer StructuredBuffer
// Hack matrix multiplication order for WebGPU (row-major vs column-major bug?)
#define PROJECT_POINT(p, m) mul(m, p)
// Stenil8 is in Red channel on WebGPU
#define STENCIL_BUFFER_SWIZZLE .r
#else
#define CAN_USE_TYPED_BUFFER_LOADS 1
#define PROJECT_POINT(p, m) mul(p, m)
#endif
// Compiler attributes
@@ -144,9 +152,9 @@ float4 LoadTextureWGSL(Texture2D tex, float2 uv)
tex.GetDimensions(size.x, size.y);
return tex.Load(uint3(size * uv, 0));
}
#define SAMPLE_RT_LOAD(rt, texCoord) LoadTextureWGSL(rt, texCoord)
#define SAMPLE_RT_DEPTH(rt, texCoord) LoadTextureWGSL(rt, texCoord).r
#else
#define SAMPLE_RT_LOAD(rt, texCoord) SAMPLE_RT(rt, texCoord)
#define SAMPLE_RT_DEPTH(rt, texCoord) SAMPLE_RT(rt, texCoord).r
#endif
#define HDR_CLAMP_MAX 65472.0
#define PI 3.1415926535897932
+1 -1
View File
@@ -21,7 +21,7 @@ META_VS(true, FEATURE_LEVEL_ES2)
VS2PS VS(float3 Position : POSITION, float4 Color : COLOR)
{
VS2PS output;
output.Position = mul(float4(Position, 1), ViewProjection);
output.Position = PROJECT_POINT(float4(Position, 1), ViewProjection);
output.Position.z += ClipPosZBias;
output.Color = Color;
return output;
+1 -1
View File
@@ -34,7 +34,7 @@ META_PERMUTATION_1(VOLUMETRIC_FOG=1)
float4 PS_Fog(Quad_VS2PS input) : SV_Target0
{
// Get world space position at given pixel coordinate
float rawDepth = SAMPLE_RT_LOAD(Depth, input.TexCoord).r;
float rawDepth = SAMPLE_RT_DEPTH(Depth, input.TexCoord);
GBufferData gBufferData = GetGBufferData();
float3 viewPos = GetViewPos(gBufferData, input.TexCoord, rawDepth);
float3 worldPos = mul(float4(viewPos, 1), gBufferData.InvViewMatrix).xyz;
+1 -1
View File
@@ -56,7 +56,7 @@ float3 GetWorldPos(GBufferData gBuffer, float2 uv, float deviceDepth)
// Sample raw device depth buffer
float SampleZ(float2 uv)
{
return SAMPLE_RT_LOAD(Depth, uv).r;
return SAMPLE_RT_DEPTH(Depth, uv);
}
// Sample linear depth
+2 -2
View File
@@ -31,7 +31,7 @@ VS2PS VS(Render2DVertex input)
if ((int)input.CustomDataAndClipOrigin.y & RENDER2D_FEATURE_VERTEX_SNAPPING)
input.Position = (float2)(int2)input.Position;
output.Position = mul(float4(input.Position, 0, 1), ViewProjection);
output.Position = PROJECT_POINT(float4(input.Position, 0, 1), ViewProjection);
output.Color = input.Color;
output.TexCoord = input.TexCoord;
output.ClipOriginAndPos = float4(input.CustomDataAndClipOrigin.zw, input.Position);
@@ -116,7 +116,7 @@ float4 PS_Downscale(Quad_VS2PS input) : SV_Target0
{
float2 boundsPos = input.TexCoord * Bounds.zw + Bounds.xy;
float4 clipPos = mul(float4(boundsPos, 0, 1), ViewProjection);
float4 clipPos = PROJECT_POINT(float4(boundsPos, 0, 1), ViewProjection);
clipPos.xy /= clipPos.w;
float2 uvPos = ClipToUv(clipPos.xy);
+17
View File
@@ -96,4 +96,21 @@ float4 TextureGatherBlue(Texture2D tex, SamplerState sam, float2 uv)
#endif
}
float4 TextureGatherDepth(Texture2D tex, float2 uv)
{
#if defined(WGSL)
// WebGPU doesn't allow to sample depth texture with regular sampler, need to use Load instead of Sample and get texture size for UV to pixel coordinate conversion
uint2 size;
tex.GetDimensions(size.x, size.y);
uint2 coord = (uint2)((float2)size * uv - 0.5f);
float x = tex.Load(uint3(coord + uint2(0, 1), 0)).x;
float y = tex.Load(uint3(coord + uint2(1, 1), 0)).x;
float z = tex.Load(uint3(coord + uint2(1, 0), 0)).x;
float w = tex.Load(uint3(coord + uint2(0, 0), 0)).x;
return float4(x, y, z, w);
#else
return TextureGatherRed(tex, SamplerPointClamp, uv);
#endif
}
#endif
+1 -1
View File
@@ -34,7 +34,7 @@ META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true)
Model_VS2PS VS_Model(ModelInput_PosOnly input)
{
Model_VS2PS output;
output.Position = mul(float4(input.Position.xyz, 1), WVP);
output.Position = PROJECT_POINT(float4(input.Position.xyz, 1), WVP);
output.ScreenPos = output.Position;
return output;
}
+4 -4
View File
@@ -37,7 +37,7 @@ META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_CameraMotionVectors(Quad_VS2PS input) : SV_Target
{
// Get the pixel world space position
float deviceDepth = SAMPLE_RT(Input0, input.TexCoord).r;
float deviceDepth = SAMPLE_RT_DEPTH(Input0, input.TexCoord);
GBufferData gBufferData = GetGBufferData();
float4 worldPos = float4(GetWorldPos(gBufferData, input.TexCoord, deviceDepth), 1);
@@ -204,7 +204,7 @@ float4 PS_MotionBlur(Quad_VS2PS input) : SV_Target
// Sample pixel depth
GBufferData gBufferData = GetGBufferData();
float pixelDepth = LinearizeZ(gBufferData, SAMPLE_RT_LOAD(Input3, input.TexCoord).x);
float pixelDepth = LinearizeZ(gBufferData, SAMPLE_RT_DEPTH(Input3, input.TexCoord));
// Calculate noise to make it look better with less samples per pixel
float noise = FullscreenGradientNoise(input.TexCoord);
@@ -229,12 +229,12 @@ float4 PS_MotionBlur(Quad_VS2PS input) : SV_Target
float weight1 = 1;
float weight2 = 1;
#else
float depth1 = LinearizeZ(gBufferData, SAMPLE_RT_LOAD(Input3, sampleUV1).x);
float depth1 = LinearizeZ(gBufferData, SAMPLE_RT_DEPTH(Input3, sampleUV1));
float2 velocity1 = Input1.SampleLevel(SamplerPointClamp, sampleUV1, 0).xy;
velocity1 = ClampVelocity(velocity1);
float velocityLength1 = length(velocity1);
float depth2 = LinearizeZ(gBufferData, SAMPLE_RT_LOAD(Input3, sampleUV2).x);
float depth2 = LinearizeZ(gBufferData, SAMPLE_RT_DEPTH(Input3, sampleUV2));
float2 velocity2 = Input1.SampleLevel(SamplerPointClamp, sampleUV2, 0).xy;
velocity2 = ClampVelocity(velocity2);
float velocityLength2 = length(velocity2);
+11 -11
View File
@@ -9,7 +9,7 @@ float2 Padding;
META_CB_END
// Use linear sampling (less texture fetches required)
#define SAMPLE(rt, texCoord) SAMPLE_RT_LINEAR(rt, texCoord)
#define SAMPLE_BLUR(rt, texCoord) SAMPLE_RT_LINEAR(rt, texCoord)
Texture2D Input : register(t0);
@@ -26,7 +26,7 @@ float PS_HalfDepth(Quad_VS2PS input)
#endif
{
// Load 4 depth values (2x2 quad)
float4 depths = TextureGatherRed(Input, SamplerPointClamp, input.TexCoord);
float4 depths = TextureGatherDepth(Input, input.TexCoord);
#if HZB_CLOSEST
return min(depths.x, min(depths.y, min(depths.z, depths.w)));
@@ -50,7 +50,7 @@ float4 PS_Blur5(Quad_VS2PS input) : SV_Target0
0.35294118
};
float4 color = SAMPLE(Input, input.TexCoord) * weights[0];
float4 color = SAMPLE_BLUR(Input, input.TexCoord) * weights[0];
UNROLL
for (int i = 1; i < 2; i++)
@@ -61,8 +61,8 @@ float4 PS_Blur5(Quad_VS2PS input) : SV_Target0
float2 texCoordOffset = float2(0, offsets[i]) * TexelSize;
#endif
color += (SAMPLE(Input, input.TexCoord + texCoordOffset)
+ SAMPLE(Input, input.TexCoord - texCoordOffset))
color += (SAMPLE_BLUR(Input, input.TexCoord + texCoordOffset)
+ SAMPLE_BLUR(Input, input.TexCoord - texCoordOffset))
* weights[i];
}
@@ -86,7 +86,7 @@ float4 PS_Blur9(Quad_VS2PS input) : SV_Target0
0.07027027
};
float4 color = SAMPLE(Input, input.TexCoord) * weights[0];
float4 color = SAMPLE_BLUR(Input, input.TexCoord) * weights[0];
UNROLL
for (int i = 1; i < 3; i++)
@@ -97,8 +97,8 @@ float4 PS_Blur9(Quad_VS2PS input) : SV_Target0
float2 texCoordOffset = float2(0, offsets[i]) * TexelSize;
#endif
color += (SAMPLE(Input, input.TexCoord + texCoordOffset)
+ SAMPLE(Input, input.TexCoord - texCoordOffset))
color += (SAMPLE_BLUR(Input, input.TexCoord + texCoordOffset)
+ SAMPLE_BLUR(Input, input.TexCoord - texCoordOffset))
* weights[i];
}
@@ -124,7 +124,7 @@ float4 PS_Blur13(Quad_VS2PS input) : SV_Target0
0.01038136
};
float4 color = SAMPLE(Input, input.TexCoord) * weights[0];
float4 color = SAMPLE_BLUR(Input, input.TexCoord) * weights[0];
UNROLL
for (int i = 1; i < 4; i++)
@@ -135,8 +135,8 @@ float4 PS_Blur13(Quad_VS2PS input) : SV_Target0
float2 texCoordOffset = float2(0, offsets[i]) * TexelSize;
#endif
color += (SAMPLE(Input, input.TexCoord + texCoordOffset)
+ SAMPLE(Input, input.TexCoord - texCoordOffset))
color += (SAMPLE_BLUR(Input, input.TexCoord + texCoordOffset)
+ SAMPLE_BLUR(Input, input.TexCoord - texCoordOffset))
* weights[i];
}
+1 -1
View File
@@ -70,7 +70,7 @@ Texture2D Source : register(t0);
META_PS(true, FEATURE_LEVEL_ES2)
float PS_DepthCopy(Quad_VS2PS input) : SV_Depth
{
return Source.SampleLevel(SamplerPointClamp, input.TexCoord * Color.xy + Color.zw, 0).r;
return SAMPLE_RT_DEPTH(Source, input.TexCoord * Color.xy + Color.zw);
}
#endif
+1 -1
View File
@@ -31,7 +31,7 @@ META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, ALIGN, PER_VERTEX, 0, true)
Model_VS2PS VS_Model(ModelInput_PosOnly input)
{
Model_VS2PS output;
output.Position = mul(float4(input.Position.xyz, 1), WVP);
output.Position = PROJECT_POINT(float4(input.Position.xyz, 1), WVP);
output.ScreenPos = output.Position;
return output;
}
+8 -8
View File
@@ -234,7 +234,7 @@ META_PS(true, FEATURE_LEVEL_ES2)
void PS_PrepareDepthsHalf(in float4 inPos : SV_POSITION, out float out0 : SV_Target0, out float out1 : SV_Target1)
{
int3 baseCoord = int3(int2(inPos.xy) * InputDepthScale, 0);
float a = g_DepthSource.Load(baseCoord, int2(0, 0)).x;
float a = g_DepthSource.Load(baseCoord).x;
float d = g_DepthSource.Load(baseCoord, int2(1, 1)).x;
GBufferData gBufferData = GetGBufferData();
@@ -325,7 +325,7 @@ float3 LoadNormal(int2 pos)
{
float3 normalEncoded = g_NormalmapSource.Load(int3(pos, 0)).xyz;
float3 normalWS = DecodeNormal(normalEncoded);
float3 normalVS = mul(normalWS, (float3x3)ViewMatrix);
float3 normalVS = PROJECT_POINT(normalWS, (float3x3)ViewMatrix);
return normalVS;
}
@@ -333,7 +333,7 @@ float3 LoadNormal(int2 pos, int2 offset)
{
float3 normalEncoded = g_NormalmapSource.Load(int3(pos, 0), offset).xyz;
float3 normalWS = DecodeNormal(normalEncoded);
float3 normalVS = mul(normalWS, (float3x3)ViewMatrix);
float3 normalVS = PROJECT_POINT(normalWS, (float3x3)ViewMatrix);
return normalVS;
}
@@ -720,21 +720,21 @@ float2 SampleBlurred(float4 inPos, float2 coord)
// Edge-sensitive blur
META_PS(true, FEATURE_LEVEL_ES2)
float2 PS_SmartBlur(in float4 inPos : SV_POSITION, in float2 inUV : TEXCOORD0) : SV_Target
float2 PS_SmartBlur(in float4 inPos : SV_POSITION, in noperspective float2 inUV : TEXCOORD0) : SV_Target
{
return SampleBlurred(inPos, inUV);
}
// Edge-sensitive blur (wider kernel)
META_PS(true, FEATURE_LEVEL_ES2)
float2 PS_SmartBlurWide(in float4 inPos : SV_POSITION, in float2 inUV : TEXCOORD0) : SV_Target
float2 PS_SmartBlurWide(in float4 inPos : SV_POSITION, in noperspective float2 inUV : TEXCOORD0) : SV_Target
{
return SampleBlurredWide(inPos, inUV);
}
// Edge-ignorant blur in x and y directions, 9 pixels touched (for the lowest quality level 0)
META_PS(true, FEATURE_LEVEL_ES2)
float2 PS_NonSmartBlur(in float4 inPos : SV_POSITION, in float2 inUV : TEXCOORD0) : SV_Target
float2 PS_NonSmartBlur(in float4 inPos : SV_POSITION, in noperspective float2 inUV : TEXCOORD0) : SV_Target
{
float2 halfPixel = HalfViewportPixelSize * 0.5f;
@@ -751,7 +751,7 @@ float2 PS_NonSmartBlur(in float4 inPos : SV_POSITION, in float2 inUV : TEXCOORD0
// Edge-ignorant blur & apply (for the lowest quality level 0)
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_Apply(in float4 inPos : SV_POSITION, in float2 inUV : TEXCOORD0) : SV_Target
float4 PS_Apply(in float4 inPos : SV_POSITION, in noperspective float2 inUV : TEXCOORD0) : SV_Target
{
float a = g_FinalSSAO.SampleLevel(SamplerLinearClamp, float3(inUV.xy, 0), 0.0).x;
float b = g_FinalSSAO.SampleLevel(SamplerLinearClamp, float3(inUV.xy, 1), 0.0).x;
@@ -765,7 +765,7 @@ float4 PS_Apply(in float4 inPos : SV_POSITION, in float2 inUV : TEXCOORD0) : SV_
// Edge-ignorant blur & apply, skipping half pixels in checkerboard pattern
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_ApplyHalf(in float4 inPos : SV_POSITION, in float2 inUV : TEXCOORD0) : SV_Target
float4 PS_ApplyHalf(in float4 inPos : SV_POSITION, in noperspective float2 inUV : TEXCOORD0) : SV_Target
{
float a = g_FinalSSAO.SampleLevel(SamplerLinearClamp, float3(inUV.xy, 0), 0.0).x;
float d = g_FinalSSAO.SampleLevel(SamplerLinearClamp, float3(inUV.xy, 3), 0.0).x;
+3 -3
View File
@@ -18,7 +18,7 @@ float2 ClipToUv(float2 clipPos)
// go into clip space (-1:1 from bottom/left to up/right)
float3 ProjectWorldToClip(float3 wsPos, float4x4 viewProjectionMatrix)
{
float4 clipPos = mul(float4(wsPos, 1), viewProjectionMatrix);
float4 clipPos = PROJECT_POINT(float4(wsPos, 1), viewProjectionMatrix);
return clipPos.xyz / clipPos.w;
}
@@ -74,7 +74,7 @@ float3 TraceScreenSpaceReflection(
#endif
// Calculate view space normal vector
float3 normalVS = mul(gBuffer.Normal, (float3x3)viewMatrix);
float3 normalVS = PROJECT_POINT(gBuffer.Normal, (float3x3)viewMatrix);
float3 reflectVS = normalize(reflect(gBuffer.ViewPos, normalVS));
if (reflectVS.z < 0.001f)
return 0; // Ray goes towards the view
@@ -116,7 +116,7 @@ float3 TraceScreenSpaceReflection(
while (currSampleIndex < numSamples)
{
// Sample depth buffer and calculate depth difference
float currSample = SAMPLE_RT_LOAD(depthBuffer, currOffset.xy).r;
float currSample = SAMPLE_RT_DEPTH(depthBuffer, currOffset.xy);
float depthDiff = currOffset.z - currSample;
// Check intersection
+3 -3
View File
@@ -37,8 +37,8 @@ float RayCastScreenSpaceShadow(GBufferData gBufferData, GBufferSample gBuffer, f
#endif
float distanceFade = 1 - saturate(pow(length(gBuffer.WorldPos - gBufferData.ViewPos) / ContactShadowsDistance, 2));
float maxShadowLength = gBufferData.InvProjectionMatrix[1][1] * gBuffer.ViewPos.z * rayLength * distanceFade;
float4 rayStartCS = mul(float4(rayStartWS, 1), ViewProjectionMatrix);
float4 rayEndCS = mul(float4(rayStartWS + rayDirWS * maxShadowLength, 1), ViewProjectionMatrix);
float4 rayStartCS = PROJECT_POINT(float4(rayStartWS, 1), ViewProjectionMatrix);
float4 rayEndCS = PROJECT_POINT(float4(rayStartWS + rayDirWS * maxShadowLength, 1), ViewProjectionMatrix);
float4 rayStepCS = (rayEndCS - rayStartCS) / maxSteps;
float4 rayCS = rayStartCS + rayStepCS;
float lightAmountMax = 0;
@@ -65,7 +65,7 @@ META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true)
Model_VS2PS VS_Model(ModelInput_PosOnly input)
{
Model_VS2PS output;
output.Position = mul(float4(input.Position.xyz, 1), WVP);
output.Position = PROJECT_POINT(float4(input.Position.xyz, 1), WVP);
output.ScreenPos = output.Position;
return output;
}
+8 -6
View File
@@ -17,17 +17,19 @@
#include "./Flax/Random.hlsl"
#endif
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5 || defined(WGSL)
#define SAMPLE_SHADOW_MAP(shadowMap, shadowUV, sceneDepth) shadowMap.SampleCmpLevelZero(ShadowSamplerLinear, shadowUV, sceneDepth)
#define SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowUV, texelOffset, sceneDepth) shadowMap.SampleCmpLevelZero(ShadowSamplerLinear, shadowUV, sceneDepth, texelOffset)
#else
#define SAMPLE_SHADOW_MAP(shadowMap, shadowUV, sceneDepth) (sceneDepth < shadowMap.SampleLevel(SamplerLinearClamp, shadowUV, 0).r)
#define SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowUV, texelOffset, sceneDepth) (sceneDepth < shadowMap.SampleLevel(SamplerLinearClamp, shadowUV, 0, texelOffset).r)
#endif
#if VULKAN || FEATURE_LEVEL < FEATURE_LEVEL_SM5
#define SAMPLE_SHADOW_MAP_SAMPLER SamplerPointClamp
#if defined(WGSL)
#define LOAD_SHADOW_MAP(shadowMap, shadowUV) SAMPLE_RT_DEPTH(shadowMap, shadowUV)
#elif VULKAN || FEATURE_LEVEL < FEATURE_LEVEL_SM5
#define LOAD_SHADOW_MAP(shadowMap, shadowUV) shadowMap.SampleLevel(SamplerPointClamp, shadowUV, 0).r
#else
#define SAMPLE_SHADOW_MAP_SAMPLER SamplerLinearClamp
#define LOAD_SHADOW_MAP(shadowMap, shadowUV) shadowMap.SampleLevel(SamplerLinearClamp, shadowUV, 0).r
#endif
float4 GetShadowMask(ShadowSample shadow)
@@ -236,7 +238,7 @@ ShadowSample SampleDirectionalLightShadowCascade(LightData light, Buffer<float4>
{
float opacity = gBuffer.CustomData.a;
shadowMapUV = GetLightShadowAtlasUV(shadow, shadowTile, gBuffer.WorldPos, shadowPosition);
float shadowMapDepth = shadowMap.SampleLevel(SAMPLE_SHADOW_MAP_SAMPLER, shadowMapUV, 0).r;
float shadowMapDepth = LOAD_SHADOW_MAP(shadowMap, shadowMapUV);
result.TransmissionShadow = CalculateSubsurfaceOcclusion(opacity, shadowPosition.z, shadowMapDepth);
result.TransmissionShadow = PostProcessShadow(shadow, result.TransmissionShadow);
}
@@ -374,7 +376,7 @@ ShadowSample SampleLocalLightShadow(LightData light, Buffer<float4> shadowsBuffe
{
float opacity = gBuffer.CustomData.a;
shadowMapUV = GetLightShadowAtlasUV(shadow, shadowTile, gBuffer.WorldPos, shadowPosition);
float shadowMapDepth = shadowMap.SampleLevel(SAMPLE_SHADOW_MAP_SAMPLER, shadowMapUV, 0).r;
float shadowMapDepth = LOAD_SHADOW_MAP(shadowMap, shadowMapUV);
result.TransmissionShadow = CalculateSubsurfaceOcclusion(opacity, shadowPosition.z, shadowMapDepth);
result.TransmissionShadow = PostProcessShadow(shadow, result.TransmissionShadow);
}
+2 -2
View File
@@ -32,7 +32,7 @@ MaterialInput VS(ModelInput_PosOnly input)
MaterialInput output;
// Compute vertex position
output.Position = mul(float4(input.Position.xyz, 1), WorldViewProjection);
output.Position = PROJECT_POINT(float4(input.Position.xyz, 1), WorldViewProjection);
output.ScreenPos = output.Position;
return output;
@@ -47,7 +47,7 @@ GBufferOutput PS_Sky(MaterialInput input)
// Calculate view vector (unproject at the far plane)
GBufferData gBufferData = GetGBufferData();
float4 clipPos = float4(input.ScreenPos.xy / input.ScreenPos.w, 1.0, 1.0);
clipPos = mul(clipPos, InvViewProjection);
clipPos = PROJECT_POINT(clipPos, InvViewProjection);
float3 worldPos = clipPos.xyz / clipPos.w;
float3 viewVector = normalize(worldPos - gBufferData.ViewPos);
+2 -2
View File
@@ -35,7 +35,7 @@ float4 PS(Quad_VS2PS input) : SV_Target0
float2 velocity = SAMPLE_RT_LINEAR(MotionVectors, input.TexCoord).xy;
float velocityLength = length(velocity);
float2 prevUV = input.TexCoord - velocity;
float prevDepth = LinearizeZ(GBuffer, SAMPLE_RT_LOAD(Depth, prevUV).r);
float prevDepth = LinearizeZ(GBuffer, SAMPLE_RT_DEPTH(Depth, prevUV));
// Find the closest pixel in 3x3 neighborhood
float currentDepth = 1;
@@ -55,7 +55,7 @@ float4 PS(Quad_VS2PS input) : SV_Target0
neighborhoodMax = max(neighborhoodMax, neighbor);
neighborhoodSum += neighbor;
float neighborDepth = LinearizeZ(GBuffer, SAMPLE_RT_LOAD(Depth, sampleUV).r);
float neighborDepth = LinearizeZ(GBuffer, SAMPLE_RT_DEPTH(Depth, sampleUV));
float depthDiff = abs(max(neighborDepth - prevDepth, 0));
minDepthDiff = min(minDepthDiff, depthDiff);
if (x == 0 && y == 0)