// Copyright (c) Wojciech Figat. All rights reserved. #if GRAPHICS_API_WEBGPU #include "GPUTextureWebGPU.h" #include "RenderToolsWebGPU.h" #include "Engine/Core/Log.h" #include "Engine/Graphics/PixelFormatExtensions.h" void GPUTextureViewWebGPU::Create(WGPUTexture texture, WGPUTextureViewDescriptor const* desc) { if (View) wgpuTextureViewRelease(View); Texture = texture; View = wgpuTextureCreateView(texture, desc); if (!View) { #if GPU_ENABLE_RESOURCE_NAMING LOG(Error, "Failed to create a view for texture '{}'", GetParent() ? GetParent()->GetName() : StringView::Empty); #endif } } void GPUTextureViewWebGPU::Release() { if (View) { wgpuTextureViewRelease(View); View = nullptr; } Texture = nullptr; } bool GPUTextureWebGPU::OnInit() { // Create texture WGPUTextureDescriptor textureDesc = WGPU_TEXTURE_DESCRIPTOR_INIT; #if GPU_ENABLE_RESOURCE_NAMING _name.Set(_namePtr, _nameSize); textureDesc.label = { _name.Get(), (size_t)_name.Length() }; #endif if (!IsDepthStencil()) textureDesc.usage |= WGPUTextureUsage_CopyDst; if (IsStaging()) textureDesc.usage |= WGPUTextureUsage_CopySrc | WGPUTextureUsage_CopyDst; if (IsShaderResource()) textureDesc.usage |= WGPUTextureUsage_TextureBinding; if (IsUnorderedAccess()) textureDesc.usage |= WGPUTextureUsage_StorageBinding; if (IsRenderTarget() || IsDepthStencil()) 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; break; case TextureDimensions::VolumeTexture: _viewDimension = WGPUTextureViewDimension_3D; textureDesc.dimension = WGPUTextureDimension_3D; break; case TextureDimensions::CubeTexture: _viewDimension = IsArray() ? WGPUTextureViewDimension_CubeArray : WGPUTextureViewDimension_Cube; textureDesc.dimension = WGPUTextureDimension_2D; textureDesc.size.depthOrArrayLayers *= 6; // Each cubemap uses 6 array slices break; } textureDesc.format = RenderToolsWebGPU::ToTextureFormat(Format()); textureDesc.mipLevelCount = _desc.MipLevels; textureDesc.sampleCount = (uint32_t)_desc.MultiSampleLevel; textureDesc.viewFormats = &textureDesc.format; textureDesc.viewFormatCount = 1; _format = textureDesc.format; _usage = textureDesc.usage; Texture = wgpuDeviceCreateTexture(_device->Device, &textureDesc); if (!Texture) return true; // Update memory usage _memoryUsage = calculateMemoryUsage(); // Initialize handles to the resource if (IsRegularTexture()) { // 'Regular' texture is using only one handle (texture/cubemap) _handlesPerSlice.Resize(1, false); } else { // Create all handles InitHandles(); } // Setup metadata for the views bool hasStencil = PixelFormatExtensions::HasStencil(Format()); if (hasStencil) { _handleArray.HasStencil = hasStencil; _handleVolume.HasStencil = hasStencil; _handleReadOnlyDepth.HasStencil = hasStencil; _handleStencil.HasStencil = hasStencil; for (auto& e : _handlesPerSlice) e.HasStencil = hasStencil; for (auto& q : _handlesPerMip) for (auto& e : q) e.HasStencil = hasStencil; } return false; } 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.dimension = _viewDimension; viewDesc.baseMipLevel = MipLevels() - ResidentMipLevels(); viewDesc.mipLevelCount = ResidentMipLevels(); viewDesc.arrayLayerCount = ArraySize(); viewDesc.aspect = WGPUTextureAspect_All; GPUTextureViewWebGPU& view = IsVolume() ? _handleVolume : _handlesPerSlice[0]; if (view.GetParent() == nullptr) view.Init(this, _desc.Format, _desc.MultiSampleLevel); view.Create(Texture, &viewDesc); } void GPUTextureWebGPU::OnReleaseGPU() { _handlesPerMip.Resize(0, false); _handlesPerSlice.Resize(0, false); _handleArray.Release(); _handleVolume.Release(); _handleReadOnlyDepth.Release(); _handleStencil.Release(); if (Texture) { wgpuTextureDestroy(Texture); wgpuTextureRelease(Texture); Texture = nullptr; } #if GPU_ENABLE_RESOURCE_NAMING _name.Clear(); #endif // Base GPUTexture::OnReleaseGPU(); } void GPUTextureWebGPU::InitHandles() { WGPUTextureViewDescriptor viewDesc = WGPU_TEXTURE_VIEW_DESCRIPTOR_INIT; #if GPU_ENABLE_RESOURCE_NAMING viewDesc.label = { _name.Get(), (size_t)_name.Length() }; #endif viewDesc.format = _format; viewDesc.usage = _usage; viewDesc.dimension = _viewDimension; viewDesc.mipLevelCount = MipLevels(); viewDesc.arrayLayerCount = ArraySize(); viewDesc.aspect = WGPUTextureAspect_All; auto format = Format(); auto msaa = MultiSampleLevel(); if (IsVolume()) { // Create handle for whole 3d texture { auto& view = _handleVolume; view.Init(this, format, msaa); view.Create(Texture, &viewDesc); } // Init per slice views _handlesPerSlice.Resize(Depth(), false); viewDesc.dimension = WGPUTextureViewDimension_2D; if (_desc.HasPerSliceViews() && IsRenderTarget()) { for (int32 sliceIndex = 0; sliceIndex < Depth(); sliceIndex++) { auto& view = _handlesPerSlice[sliceIndex]; view.Init(this, format, msaa); view.Create(Texture, &viewDesc); view.DepthSlice = sliceIndex; } } viewDesc.dimension = _viewDimension; } else if (IsArray()) { // Create whole array handle { auto& view = _handleArray; view.Init(this, format, msaa); view.Create(Texture, &viewDesc); } // Create per array slice handles _handlesPerSlice.Resize(ArraySize(), false); viewDesc.dimension = WGPUTextureViewDimension_2D; for (int32 arrayIndex = 0; arrayIndex < _handlesPerSlice.Count(); arrayIndex++) { viewDesc.baseArrayLayer = arrayIndex; viewDesc.arrayLayerCount = 1; auto& view = _handlesPerSlice[arrayIndex]; view.Init(this, format, msaa); view.Create(Texture, &viewDesc); } viewDesc.baseArrayLayer = 0; viewDesc.arrayLayerCount = MipLevels(); viewDesc.dimension = _viewDimension; } else { // Create single handle for the whole texture _handlesPerSlice.Resize(1, false); auto& view = _handlesPerSlice[0]; view.Init(this, format, msaa); view.Create(Texture, &viewDesc); } // Init per mip map handles if (HasPerMipViews()) { // Init handles _handlesPerMip.Resize(ArraySize(), false); viewDesc.dimension = WGPUTextureViewDimension_2D; for (int32 arrayIndex = 0; arrayIndex < _handlesPerMip.Count(); arrayIndex++) { auto& slice = _handlesPerMip[arrayIndex]; slice.Resize(MipLevels(), false); viewDesc.baseArrayLayer = arrayIndex; viewDesc.arrayLayerCount = 1; viewDesc.mipLevelCount = 1; for (int32 mipIndex = 0; mipIndex < slice.Count(); mipIndex++) { auto& view = slice[mipIndex]; viewDesc.baseMipLevel = mipIndex; view.Init(this, format, msaa); view.Create(Texture, &viewDesc); } } viewDesc.dimension = _viewDimension; } // Read-only depth-stencil if (EnumHasAnyFlags(_desc.Flags, GPUTextureFlags::ReadOnlyDepthView)) { auto& view = _handleReadOnlyDepth; view.Init(this, format, msaa); view.Create(Texture, &viewDesc); view.ReadOnly = true; } // Stencil view if (IsDepthStencil() && IsShaderResource() && PixelFormatExtensions::HasStencil(format)) { PixelFormat stencilFormat = format; switch (format) { case PixelFormat::D24_UNorm_S8_UInt: stencilFormat = PixelFormat::X24_Typeless_G8_UInt; break; case PixelFormat::D32_Float_S8X24_UInt: stencilFormat = PixelFormat::X32_Typeless_G8X24_UInt; break; } viewDesc.aspect = WGPUTextureAspect_StencilOnly; viewDesc.format = WGPUTextureFormat_Stencil8; _handleStencil.Init(this, stencilFormat, msaa); _handleStencil.Create(Texture, &viewDesc); } } bool GPUTextureWebGPU::GetData(int32 arrayIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch) { // TODO: no implemented return true; } #endif