Merge branch 'master' into PressGToGameModeAndPToNavigate

This commit is contained in:
Saas
2025-12-11 21:04:00 +01:00
committed by GitHub
780 changed files with 62336 additions and 6539 deletions

View File

@@ -5,6 +5,7 @@
#include "GPUTask.h"
#include "GPUTasksManager.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/GPUPass.h"
#include "Engine/Profiler/ProfilerCPU.h"
DefaultGPUTasksExecutor::DefaultGPUTasksExecutor()
@@ -30,6 +31,9 @@ void DefaultGPUTasksExecutor::FrameBegin()
// Default implementation performs async operations on start of the frame which is synchronized with a rendering thread
GPUTask* buffer[32];
const int32 count = GPUDevice::Instance->GetTasksManager()->RequestWork(buffer, 32);
if (count == 0)
return;
GPUMemoryPass pass(_context->GPU);
for (int32 i = 0; i < count; i++)
{
_context->Run(buffer[i]);

View File

@@ -70,7 +70,7 @@
// Default depth buffer pixel format
#ifndef GPU_DEPTH_BUFFER_PIXEL_FORMAT
#define GPU_DEPTH_BUFFER_PIXEL_FORMAT PixelFormat::D32_Float
#define GPU_DEPTH_BUFFER_PIXEL_FORMAT PixelFormat::D24_UNorm_S8_UInt
#endif
// Enable/disable gpu resources naming

View File

@@ -340,8 +340,15 @@ API_ENUM(Attributes="Flags") enum class GPUResourceMapMode
/// The resource is mapped for reading and writing.
/// </summary>
ReadWrite = Read | Write,
/// <summary>
/// Flag that indicates mapping should fail with no data if the resource is still used by the GPU. Otherwise, CPU will wait for the GPU execution.
/// </summary>
NoWait = 0x04,
};
DECLARE_ENUM_OPERATORS(GPUResourceMapMode);
/// <summary>
/// Primitives types.
/// </summary>

View File

@@ -15,6 +15,7 @@
#include "Engine/Debug/Exceptions/ArgumentNullException.h"
#include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/Enums.h"
#include "Engine/Threading/ThreadPoolTask.h"
#include "Engine/Threading/Threading.h"
@@ -173,7 +174,7 @@ GPUBuffer::GPUBuffer()
: GPUResource(SpawnParams(Guid::New(), TypeInitializer))
{
// Buffer with size 0 is considered to be invalid
_desc.Size = 0;
_desc.Clear();
}
bool GPUBuffer::IsStaging() const
@@ -188,6 +189,8 @@ bool GPUBuffer::IsDynamic() const
bool GPUBuffer::Init(const GPUBufferDescription& desc)
{
PROFILE_MEM(GraphicsBuffers);
// Validate description
#if !BUILD_RELEASE
#define GET_NAME() GetName()
@@ -242,6 +245,15 @@ bool GPUBuffer::Init(const GPUBufferDescription& desc)
return true;
}
#if COMPILE_WITH_PROFILER
auto group = ProfilerMemory::Groups::GraphicsBuffers;
if (EnumHasAnyFlags(_desc.Flags, GPUBufferFlags::VertexBuffer))
group = ProfilerMemory::Groups::GraphicsVertexBuffers;
else if (EnumHasAnyFlags(_desc.Flags, GPUBufferFlags::IndexBuffer))
group = ProfilerMemory::Groups::GraphicsIndexBuffers;
ProfilerMemory::IncrementGroup(group, _memoryUsage);
#endif
return false;
}
@@ -476,6 +488,15 @@ GPUResourceType GPUBuffer::GetResourceType() const
void GPUBuffer::OnReleaseGPU()
{
#if COMPILE_WITH_PROFILER
auto group = ProfilerMemory::Groups::GraphicsBuffers;
if (EnumHasAnyFlags(_desc.Flags, GPUBufferFlags::VertexBuffer))
group = ProfilerMemory::Groups::GraphicsVertexBuffers;
else if (EnumHasAnyFlags(_desc.Flags, GPUBufferFlags::IndexBuffer))
group = ProfilerMemory::Groups::GraphicsIndexBuffers;
ProfilerMemory::IncrementGroup(group, _memoryUsage);
#endif
_desc.Clear();
_isLocked = false;
}

View File

@@ -1,9 +1,41 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using System;
using System.Runtime.CompilerServices;
using FlaxEngine.Interop;
namespace FlaxEngine
{
partial class GPUDevice
{
/// <summary>
/// Gets the list with all active GPU resources.
/// </summary>
public GPUResource[] Resources
{
get
{
IntPtr ptr = Internal_GetResourcesInternal(__unmanagedPtr);
ManagedArray array = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(ptr).Target);
return NativeInterop.GCHandleArrayToManagedArray<GPUResource>(array);
}
}
/// <summary>
/// Gets the list with all active GPU resources.
/// </summary>
/// <param name="buffer">Output buffer to fill with resource pointers. Can be provided by a user to avoid memory allocation. Buffer might be larger than actual list size. Use <paramref name="count"/> for actual item count.></param>
/// <param name="count">Amount of valid items inside <paramref name="buffer"/>.</param>
public void GetResources(ref GPUResource[] buffer, out int count)
{
count = 0;
IntPtr ptr = Internal_GetResourcesInternal(__unmanagedPtr);
ManagedArray array = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(ptr).Target);
buffer = NativeInterop.GCHandleArrayToManagedArray<GPUResource>(array, buffer);
count = buffer.Length;
}
}
partial struct GPUBufferDescription : IEquatable<GPUBufferDescription>
{
/// <summary>

View File

@@ -334,11 +334,12 @@ public:
/// Creates argument buffer description.
/// </summary>
/// <param name="size">The size (in bytes).</param>
/// <param name="additionalFlags">The additional bindings (for example, to use as UAV, pass <see cref="GPUBufferFlags::UnorderedAccess" />).</param>
/// <param name="usage">The usage.</param>
/// <returns>The buffer description.</returns>
static GPUBufferDescription Argument(int32 size, GPUResourceUsage usage = GPUResourceUsage::Default)
static GPUBufferDescription Argument(int32 size, GPUResourceUsage usage = GPUResourceUsage::Default, GPUBufferFlags additionalFlags = GPUBufferFlags::None)
{
return Buffer(size, GPUBufferFlags::Argument, PixelFormat::Unknown, nullptr, 0, usage);
return Buffer(size, GPUBufferFlags::Argument | additionalFlags, PixelFormat::R32_UInt, nullptr, sizeof(uint32), usage);
}
/// <summary>

View File

@@ -20,6 +20,7 @@ void GPUContext::LogInvalidResourceUsage(int32 slot, const GPUResourceView* view
GPUResource* resource = view ? view->GetParent() : nullptr;
const Char* resourceType = TEXT("resource");
const Char* flagType = TEXT("flags");
StringView resourceName;
if (resource)
{
switch (resource->GetResourceType())
@@ -36,6 +37,7 @@ void GPUContext::LogInvalidResourceUsage(int32 slot, const GPUResourceView* view
flagType = TEXT("GPUBufferFlags");
break;
}
resourceName = resource->GetName();
}
const Char* usage = TEXT("-");
switch (bindPoint)
@@ -53,7 +55,7 @@ void GPUContext::LogInvalidResourceUsage(int32 slot, const GPUResourceView* view
usage = TEXT("render target");
break;
}
LOG(Error, "Incorrect {} bind at slot {} as {} (ensure to setup correct {} when creating that resource)", resourceType, slot, usage, flagType);
LOG(Error, "Incorrect {} '{}' bind at slot {} as {} (ensure to setup correct {} when creating that resource)", resourceType, resourceName, slot, usage, flagType);
}
#endif
@@ -65,10 +67,14 @@ void GPUContext::FrameBegin()
void GPUContext::FrameEnd()
{
ClearState();
ResetState();
FlushState();
}
void GPUContext::OnPresent()
{
}
void GPUContext::BindSR(int32 slot, GPUTexture* t)
{
ASSERT_LOW_LAYER(t == nullptr || t->ResidentMipLevels() == 0 || t->IsShaderResource());

View File

@@ -9,6 +9,11 @@
#include "PixelFormat.h"
#include "Config.h"
#if PLATFORM_WIN32
// Fix nasty Win32 define garbage
#undef MemoryBarrier
#endif
class GPUConstantBuffer;
class GPUShaderProgramCS;
class GPUBuffer;
@@ -21,6 +26,8 @@ class GPUResourceView;
class GPUTextureView;
class GPUBufferView;
class GPUVertexLayout;
struct GPUPass;
enum class GPUResourceAccess;
// Gets the GPU texture view. Checks if pointer is not null and texture has one or more mip levels loaded.
#define GET_TEXTURE_VIEW_SAFE(t) (t && t->ResidentMipLevels() > 0 ? t->View() : nullptr)
@@ -148,6 +155,11 @@ public:
/// </summary>
virtual void FrameEnd();
/// <summary>
/// Called after performing final swapchain presentation and submitting all GPU commands.
/// </summary>
virtual void OnPresent();
public:
#if GPU_ALLOW_PROFILE_EVENTS
/// <summary>
@@ -606,7 +618,16 @@ public:
/// <summary>
/// Clears the context state.
/// </summary>
API_FUNCTION() virtual void ClearState() = 0;
DEPRECATED("Use ResetState instead")
API_FUNCTION() void ClearState()
{
ResetState();
}
/// <summary>
/// Resets the context state.
/// </summary>
API_FUNCTION() virtual void ResetState() = 0;
/// <summary>
/// Flushes the internal cached context state with a command buffer.
@@ -627,4 +648,24 @@ public:
/// Forces graphics backend to rebind descriptors after command list was used by external graphics library.
/// </summary>
virtual void ForceRebindDescriptors();
protected:
friend GPUPass;
int32 _pass = 0;
public:
// Performs resource state transition into a specific access (mask).
virtual void Transition(GPUResource* resource, GPUResourceAccess access)
{
}
// Inserts a global memory barrier on data copies between resources.
virtual void MemoryBarrier()
{
}
// Begins or ends unordered access resource overlap region that allows running different compute shader dispatches simultaneously.
virtual void OverlapUA(bool end)
{
}
};

View File

@@ -75,10 +75,39 @@ GPUPipelineState::GPUPipelineState()
{
}
#if !BUILD_RELEASE
void GPUPipelineState::GetDebugName(DebugName& name) const
{
#define GET_NAME(e) \
if (DebugDesc.e) \
{ \
GPUShaderProgram::DebugName n; \
DebugDesc.e->GetDebugName(n); \
name.Add(n.Get(), n.Count() - 1); \
name.Add('+'); \
}
GET_NAME(VS);
#if GPU_ALLOW_TESSELLATION_SHADERS
GET_NAME(HS);
GET_NAME(DS);
#endif
#if GPU_ALLOW_GEOMETRY_SHADERS
GET_NAME(GS);
#endif
GET_NAME(PS);
#undef GET_NAME
if (name.Count() != 0 && name[name.Count() - 1] == '+')
name.RemoveLast();
name.Add('\0');
}
#endif
bool GPUPipelineState::Init(const Description& desc)
{
// Cache description in debug builds
#if BUILD_DEBUG
// Cache description in development builds
#if !BUILD_RELEASE
DebugDesc = desc;
#endif
@@ -363,6 +392,8 @@ bool GPUDevice::Init()
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);
return false;
}
@@ -617,6 +648,7 @@ void GPUDevice::DrawEnd()
const double presentEnd = Platform::GetTimeSeconds();
ProfilerGPU::OnPresentTime((float)((presentEnd - presentStart) * 1000.0));
#endif
GetMainContext()->OnPresent();
_wasVSyncUsed = anyVSync;
_isRendering = false;
@@ -648,8 +680,26 @@ GPUTasksExecutor* GPUDevice::CreateTasksExecutor()
return New<DefaultGPUTasksExecutor>();
}
#if !COMPILE_WITHOUT_CSHARP
#include "Engine/Scripting/ManagedCLR/MUtils.h"
void* GPUDevice::GetResourcesInternal()
{
_resourcesLock.Lock();
MArray* result = MCore::Array::New(GPUResource::TypeInitializer.GetClass(), _resources.Count());
int32 i = 0;
for (const auto& e : _resources)
MCore::GC::WriteArrayRef(result, e->GetOrCreateManagedInstance(), i++);
_resourcesLock.Unlock();
return result;
}
#endif
void GPUDevice::Draw()
{
PROFILE_MEM(Graphics);
DrawBegin();
auto context = GetMainContext();
@@ -677,6 +727,7 @@ void GPUDevice::Draw()
void GPUDevice::Dispose()
{
RenderList::CleanupCache();
VideoOutputs.Resize(0);
VideoOutputModes.Resize(0);
}

View File

@@ -54,7 +54,40 @@ public:
};
/// <summary>
/// Describes a video output display mode.
/// Describes a video output display (monitor).
/// </summary>
API_STRUCT() struct VideoOutput
{
DECLARE_SCRIPTING_TYPE_NO_SPAWN(VideoOutputMode);
/// <summary>
/// The display name.
/// </summary>
API_FIELD() String Name;
/// <summary>
/// The native screen resolution width (in pixel).
/// </summary>
API_FIELD() uint32 Width = 0;
/// <summary>
/// The native screen resolution height (in pixel).
/// </summary>
API_FIELD() uint32 Height = 0;
/// <summary>
/// The maximum screen refresh rate (in hertz).
/// </summary>
API_FIELD() float RefreshRate = 0;
/// <summary>
/// Flag that indicates that monitor supports displaying High Dynamic Range colors.
/// </summary>
API_FIELD() bool HDR = false;
};
/// <summary>
/// Describes a video output display mode (monitor screen mode).
/// </summary>
API_STRUCT() struct VideoOutputMode
{
@@ -73,7 +106,12 @@ public:
/// <summary>
/// The screen refresh rate (in hertz).
/// </summary>
API_FIELD() uint32 RefreshRate;
API_FIELD() float RefreshRate;
/// <summary>
/// The index of the VideoOutput from the device monitors list.
/// </summary>
API_FIELD() int32 VideoOutputIndex;
};
/// <summary>
@@ -134,6 +172,11 @@ public:
/// </summary>
API_FIELD(ReadOnly) GPULimits Limits;
/// <summary>
/// The available video outputs (monitors).
/// </summary>
API_FIELD(ReadOnly) Array<VideoOutput> VideoOutputs;
/// <summary>
/// The available video output modes.
/// </summary>
@@ -236,7 +279,7 @@ public:
/// <summary>
/// Gets the list with all active GPU resources.
/// </summary>
API_PROPERTY() Array<GPUResource*> GetResources() const;
Array<GPUResource*> GetResources() const;
/// <summary>
/// Gets the GPU asynchronous work manager.
@@ -432,6 +475,12 @@ public:
/// </summary>
/// <returns>The GPU tasks executor.</returns>
virtual GPUTasksExecutor* CreateTasksExecutor();
private:
// Internal bindings
#if !COMPILE_WITHOUT_CSHARP
API_FUNCTION(NoProxy) void* GetResourcesInternal();
#endif
};
/// <summary>

View File

@@ -0,0 +1,74 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#pragma once
#include "GPUContext.h"
#include "Engine/Graphics/GPUResourceAccess.h"
/// <summary>
/// Base for GPU rendering passes that control low-level memory access and GPU resources states with usage to optimize rendering.
/// </summary>
struct FLAXENGINE_API GPUPass
{
NON_COPYABLE(GPUPass);
GPUContext* Context;
GPUPass(GPUContext* context)
: Context(context)
{
Context->_pass++;
}
~GPUPass()
{
Context->_pass--;
}
// Performs resource state transition into a specific access (mask). Can be done preemptively in the prologue of the pass to execute more efficient barriers.
void Transition(GPUResource* resource, GPUResourceAccess access)
{
Context->Transition(resource, access);
}
};
/// <summary>
/// GPU pass that manually controls memory barriers and cache flushes when performing batched copy/upload operations with GPU context. Can be used to optimize GPU buffers usage by running different copy operations simultaneously.
/// </summary>
struct FLAXENGINE_API GPUMemoryPass : GPUPass
{
GPUMemoryPass(GPUContext* context)
: GPUPass(context)
{
}
~GPUMemoryPass()
{
Context->MemoryBarrier();
}
// Inserts a global memory barrier on data copies between resources. Use to ensure all writes and before submitting another commands.
void MemoryBarrier()
{
Context->MemoryBarrier();
}
};
/// <summary>
/// GPU pass that controls memory barriers when performing batched Compute shader dispatches with GPU context. Can be used to optimize GPU utilization by running different dispatches simultaneously (by overlapping work).
/// </summary>
struct FLAXENGINE_API GPUComputePass : GPUPass
{
GPUComputePass(GPUContext* context)
: GPUPass(context)
{
Context->OverlapUA(false);
}
~GPUComputePass()
{
Context->OverlapUA(true);
}
};
// TODO: add GPUDrawPass for render targets and depth/stencil setup with optimized clear for faster drawing on tiled-GPUs (mobile)

View File

@@ -172,11 +172,14 @@ protected:
GPUPipelineState();
public:
#if BUILD_DEBUG
#if !BUILD_RELEASE
/// <summary>
/// The description of the pipeline state cached on creation in debug builds. Can be used to help with rendering crashes or issues and validation.
/// </summary>
Description DebugDesc;
typedef Array<char, InlinedAllocation<200>> DebugName;
void GetDebugName(DebugName& name) const;
#endif
#if USE_EDITOR
int32 Complexity;

View File

@@ -0,0 +1,29 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Types/BaseTypes.h"
// GPU resource access flags. Used to describe how resource can be accessed which allows GPU to optimize data layout and memory access.
enum class GPUResourceAccess
{
None = 0,
CopyRead = 1 << 0,
CopyWrite = 1 << 1,
CpuRead = 1 << 2,
CpuWrite = 1 << 3,
DepthRead = 1 << 4,
DepthWrite = 1 << 5,
DepthBuffer = DepthRead | DepthWrite,
RenderTarget = 1 << 6,
UnorderedAccess = 1 << 7,
IndirectArgs = 1 << 8,
ShaderReadCompute = 1 << 9,
ShaderReadPixel = 1 << 10,
ShaderReadNonPixel = 1 << 11,
ShaderReadGraphics = ShaderReadPixel | ShaderReadNonPixel,
Last,
All = (Last << 1) - 1,
};
DECLARE_ENUM_OPERATORS(GPUResourceAccess);

View File

@@ -20,6 +20,12 @@ public abstract class GraphicsDeviceBaseModule : EngineModule
// Enables GPU diagnostic tools (debug layer etc.)
options.PublicDefinitions.Add("GPU_ENABLE_DIAGNOSTICS");
}
if (Profiler.Use(options) && tracy.Use(options) && tracy.GPU && true)
{
// Enables GPU profiling with Tracy
options.PrivateDefinitions.Add("GPU_ENABLE_TRACY");
}
}
/// <inheritdoc />

View File

@@ -9,7 +9,10 @@
#include "Engine/Engine/CommandLine.h"
#include "Engine/Engine/EngineService.h"
#include "Engine/Profiler/ProfilerGPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#if !USE_EDITOR
#include "Engine/Render2D/Font.h"
#endif
bool Graphics::UseVSync = false;
Quality Graphics::AAQuality = Quality::Medium;
@@ -25,6 +28,7 @@ Quality Graphics::GIQuality = Quality::High;
bool Graphics::GICascadesBlending = false;
PostProcessSettings Graphics::PostProcessSettings;
bool Graphics::SpreadWorkload = true;
bool Graphics::PostProcessing::ColorGradingVolumeLUT = true;
#if GRAPHICS_API_NULL
extern GPUDevice* CreateGPUDeviceNull();
@@ -38,12 +42,6 @@ extern GPUDevice* CreateGPUDeviceDX11();
#if GRAPHICS_API_DIRECTX12
extern GPUDevice* CreateGPUDeviceDX12();
#endif
#if GRAPHICS_API_PS4
extern GPUDevice* CreateGPUDevicePS4();
#endif
#if GRAPHICS_API_PS5
extern GPUDevice* CreateGPUDevicePS5();
#endif
class GraphicsService : public EngineService
{
@@ -97,9 +95,10 @@ void Graphics::DisposeDevice()
bool GraphicsService::Init()
{
ASSERT(GPUDevice::Instance == nullptr);
PROFILE_MEM(Graphics);
// Create and initialize graphics device
Log::Logger::WriteFloor();
LOG_FLOOR();
LOG(Info, "Creating Graphics Device...");
PixelFormatExtensions::Init();
GPUDevice* device = nullptr;
@@ -163,10 +162,12 @@ bool GraphicsService::Init()
device = CreateGPUDeviceVulkan();
#endif
#if GRAPHICS_API_PS4
extern GPUDevice* CreateGPUDevicePS4();
if (!device)
device = CreateGPUDevicePS4();
#endif
#if GRAPHICS_API_PS5
extern GPUDevice* CreateGPUDevicePS5();
if (!device)
device = CreateGPUDevicePS5();
#endif
@@ -201,7 +202,7 @@ bool GraphicsService::Init()
#endif
)
{
#if !USE_EDITOR && BUILD_RELEASE && !PLATFORM_LINUX // IsDebugToolAttached seams to be enabled on many Linux machines via VK_EXT_tooling_info
#if !USE_EDITOR && BUILD_RELEASE && !PLATFORM_LINUX && !PLATFORM_CONSOLE // IsDebugToolAttached seams to be enabled on many Linux machines via VK_EXT_tooling_info
// Block graphics debugging to protect contents
Platform::Fatal(TEXT("Graphics debugger attached."));
#endif
@@ -214,7 +215,7 @@ bool GraphicsService::Init()
{
return true;
}
Log::Logger::WriteFloor();
LOG_FLOOR();
return false;
}

View File

@@ -84,6 +84,16 @@ public:
/// </summary>
API_FIELD() static bool SpreadWorkload;
public:
// Post Processing effects rendering configuration.
API_CLASS(Static, Attributes = "DebugCommand") class FLAXENGINE_API PostProcessing
{
DECLARE_SCRIPTING_TYPE_MINIMAL(PostProcessing);
// Toggles between 2D and 3D LUT texture for Color Grading.
API_FIELD() static bool ColorGradingVolumeLUT;
};
public:
/// <summary>
/// Disposes the device.

View File

@@ -17,6 +17,8 @@ PACK_STRUCT(struct DecalMaterialShaderData {
Matrix WorldMatrix;
Matrix InvWorld;
Matrix SvPositionToWorld;
Float3 Padding0;
uint32 RenderLayersMask;
});
DrawPass DecalMaterialShader::GetDrawModes() const
@@ -50,6 +52,7 @@ void DecalMaterialShader::Bind(BindParameters& params)
GPUTexture* depthBuffer = params.RenderContext.Buffers->DepthBuffer;
GPUTextureView* depthBufferView = EnumHasAnyFlags(depthBuffer->Flags(), GPUTextureFlags::ReadOnlyDepthView) ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View();
context->BindSR(0, depthBufferView);
context->BindSR(1, depthBuffer->ViewStencil());
// Setup material constants
{
@@ -68,6 +71,7 @@ void DecalMaterialShader::Bind(BindParameters& params)
-1.0f, 1.0f, 0, 1);
const Matrix svPositionToWorld = offsetMatrix * view.IVP;
Matrix::Transpose(svPositionToWorld, materialData->SvPositionToWorld);
materialData->RenderLayersMask = (uint32)drawCall.SortKey; // Provided by GBufferPass::DrawDecals
}
// Bind constants

View File

@@ -29,7 +29,7 @@ bool DeferredMaterialShader::CanUseLightmap() const
bool DeferredMaterialShader::CanUseInstancing(InstancingHandler& handler) const
{
handler = { SurfaceDrawCallHandler::GetHash, SurfaceDrawCallHandler::CanBatch, };
return true;
return _instanced;
}
void DeferredMaterialShader::Bind(BindParameters& params)
@@ -42,6 +42,8 @@ void DeferredMaterialShader::Bind(BindParameters& params)
// Setup features
const bool useLightmap = _info.BlendMode == MaterialBlendMode::Opaque && LightmapFeature::Bind(params, cb, srv);
if (_info.ShadingModel == MaterialShadingModel::CustomLit)
ForwardShadingFeature::Bind(params, cb, srv);
// Setup parameters
MaterialParameter::BindMeta bindMeta;
@@ -83,13 +85,10 @@ void DeferredMaterialShader::Bind(BindParameters& params)
if (IsRunningRadiancePass)
cullMode = CullMode::TwoSided;
#endif
if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0)
if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminant)
{
// Invert culling when scale is negative
if (cullMode == CullMode::Normal)
cullMode = CullMode::Inverted;
else
cullMode = CullMode::Normal;
cullMode = cullMode == CullMode::Normal ? CullMode::Inverted : CullMode::Normal;
}
ASSERT_LOW_LAYER(!(useSkinning && params.Instanced)); // No support for instancing skinned meshes
const auto cache = params.Instanced ? &_cacheInstanced : &_cache;
@@ -99,6 +98,7 @@ void DeferredMaterialShader::Bind(BindParameters& params)
// Bind pipeline
context->SetState(state);
context->SetStencilRef(drawCall.StencilValue);
}
void DeferredMaterialShader::Unload()
@@ -112,6 +112,9 @@ void DeferredMaterialShader::Unload()
bool DeferredMaterialShader::Load()
{
// TODO: support instancing when using ForwardShadingFeature
_instanced = _info.BlendMode == MaterialBlendMode::Opaque && _info.ShadingModel != MaterialShadingModel::CustomLit;
bool failed = false;
auto psDesc = GPUPipelineState::Description::Default;
psDesc.DepthWriteEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthWrite) == MaterialFeaturesFlags::None;
@@ -132,6 +135,10 @@ bool DeferredMaterialShader::Load()
}
#endif
psDesc.StencilEnable = true;
psDesc.StencilReadMask = 0;
psDesc.StencilPassOp = StencilOperation::Replace;
// GBuffer Pass
psDesc.VS = _shader->GetVS("VS");
failed |= psDesc.VS == nullptr;
@@ -155,6 +162,9 @@ bool DeferredMaterialShader::Load()
psDesc.PS = _shader->GetPS("PS_GBuffer");
_cache.DefaultSkinned.Init(psDesc);
psDesc.StencilEnable = false;
psDesc.StencilPassOp = StencilOperation::Keep;
#if USE_EDITOR
if (_shader->HasShader("PS_QuadOverdraw"))
{
@@ -191,6 +201,7 @@ bool DeferredMaterialShader::Load()
psDesc.DepthWriteEnable = true;
psDesc.DepthEnable = true;
psDesc.DepthFunc = ComparisonFunc::Less;
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::None;
psDesc.HS = nullptr;
psDesc.DS = nullptr;
GPUShaderProgramVS* instancedDepthPassVS;

View File

@@ -65,6 +65,7 @@ private:
private:
Cache _cache;
Cache _cacheInstanced;
bool _instanced;
public:
DeferredMaterialShader(const StringView& name)

View File

@@ -62,7 +62,7 @@ void DeformableMaterialShader::Bind(BindParameters& params)
{
Matrix::Transpose(drawCall.World, materialData->WorldMatrix);
Matrix::Transpose(drawCall.Deformable.LocalMatrix, materialData->LocalMatrix);
materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign;
materialData->WorldDeterminantSign = drawCall.WorldDeterminant ? -1.0f : 1.0f;
materialData->Segment = drawCall.Deformable.Segment;
materialData->ChunksPerSegment = drawCall.Deformable.ChunksPerSegment;
materialData->MeshMinZ = drawCall.Deformable.MeshMinZ;
@@ -84,13 +84,10 @@ void DeformableMaterialShader::Bind(BindParameters& params)
// Select pipeline state based on current pass and render mode
const bool wireframe = (_info.FeaturesFlags & MaterialFeaturesFlags::Wireframe) != MaterialFeaturesFlags::None || view.Mode == ViewMode::Wireframe;
CullMode cullMode = view.Pass == DrawPass::Depth ? CullMode::TwoSided : _info.CullMode;
if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0)
if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminant)
{
// Invert culling when scale is negative
if (cullMode == CullMode::Normal)
cullMode = CullMode::Inverted;
else
cullMode = CullMode::Normal;
cullMode = cullMode == CullMode::Normal ? CullMode::Inverted : CullMode::Normal;
}
PipelineStateCache* psCache = _cache.GetPS(view.Pass);
ASSERT(psCache);
@@ -98,6 +95,7 @@ void DeformableMaterialShader::Bind(BindParameters& params)
// Bind pipeline
context->SetState(state);
context->SetStencilRef(drawCall.StencilValue);
}
void DeformableMaterialShader::Unload()
@@ -139,10 +137,17 @@ bool DeformableMaterialShader::Load()
{
_drawModes |= DrawPass::GBuffer | DrawPass::GlobalSurfaceAtlas;
psDesc.StencilEnable = true;
psDesc.StencilReadMask = 0;
psDesc.StencilPassOp = StencilOperation::Replace;
// GBuffer Pass
psDesc.VS = _shader->GetVS("VS_SplineModel");
psDesc.PS = _shader->GetPS("PS_GBuffer");
_cache.Default.Init(psDesc);
psDesc.StencilEnable = false;
psDesc.StencilPassOp = StencilOperation::Keep;
}
else
{

View File

@@ -25,7 +25,7 @@ DrawPass ForwardMaterialShader::GetDrawModes() const
bool ForwardMaterialShader::CanUseInstancing(InstancingHandler& handler) const
{
handler = { SurfaceDrawCallHandler::GetHash, SurfaceDrawCallHandler::CanBatch, };
return true;
return false; // TODO: support instancing when using ForwardShadingFeature
}
void ForwardMaterialShader::Bind(BindParameters& params)
@@ -80,13 +80,10 @@ void ForwardMaterialShader::Bind(BindParameters& params)
if (IsRunningRadiancePass)
cullMode = CullMode::TwoSided;
#endif
if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0)
if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminant)
{
// Invert culling when scale is negative
if (cullMode == CullMode::Normal)
cullMode = CullMode::Inverted;
else
cullMode = CullMode::Normal;
cullMode = cullMode == CullMode::Normal ? CullMode::Inverted : CullMode::Normal;
}
ASSERT_LOW_LAYER(!(useSkinning && params.Instanced)); // No support for instancing skinned meshes
const auto cacheObj = params.Instanced ? &_cacheInstanced : &_cache;
@@ -198,5 +195,10 @@ bool ForwardMaterialShader::Load()
psDesc.VS = _shader->GetVS("VS_Skinned");
_cache.DepthSkinned.Init(psDesc);
#if PLATFORM_PS5
// Fix shader binding issues on forward shading materials on PS5
_drawModes = DrawPass::None;
#endif
return false;
}

View File

@@ -24,7 +24,7 @@ PACK_STRUCT(struct GUIMaterialShaderData {
Float4 ScreenSize;
Float4 ViewSize;
Float3 ViewPadding0;
float UnscaledTimeParam;
float ScaledTimeParam;
});
void GUIMaterialShader::Bind(BindParameters& params)
@@ -58,7 +58,7 @@ void GUIMaterialShader::Bind(BindParameters& params)
materialData->ViewFar = 0.0f;
materialData->ViewDir = Float3::Forward;
materialData->TimeParam = params.Time;
materialData->UnscaledTimeParam = params.UnscaledTime;
materialData->ScaledTimeParam = params.ScaledTime;
materialData->ViewInfo = Float4::Zero;
auto& viewport = Render2D::GetViewport();
materialData->ScreenSize = Float4(viewport.Width, viewport.Height, 1.0f / viewport.Width, 1.0f / viewport.Height);

View File

@@ -148,7 +148,7 @@ public:
const ::DrawCall* DrawCall = nullptr;
MaterialParamsLink* ParamsLink = nullptr;
void* CustomData = nullptr;
float Time, UnscaledTime;
float Time, ScaledTime;
bool Instanced = false;
/// <summary>

View File

@@ -103,6 +103,11 @@ API_ENUM() enum class MaterialShadingModel : byte
/// The foliage material. Intended for foliage materials like leaves and grass that need light scattering to transport simulation through the thin object.
/// </summary>
Foliage = 3,
/// <summary>
/// The custom lit shader that calculates own lighting such as Cel Shading. It has access to the scene lights data during both GBuffer and Forward pass rendering.
/// </summary>
CustomLit = 5,
};
/// <summary>
@@ -439,6 +444,16 @@ API_ENUM() enum class MaterialSceneTextures
/// The scene world-space position (relative to the render view origin).
/// </summary>
WorldPosition = 11,
/// <summary>
/// The scene stencil.
/// </summary>
SceneStencil = 12,
/// <summary>
/// The object layer index.
/// </summary>
ObjectLayer = 13,
};
/// <summary>

View File

@@ -15,6 +15,7 @@
#include "Engine/Renderer/GlobalSignDistanceFieldPass.h"
#include "Engine/Scripting/Enums.h"
#include "Engine/Streaming/Streaming.h"
#include "Engine/Profiler/ProfilerMemory.h"
bool MaterialInfo8::operator==(const MaterialInfo8& other) const
{
@@ -392,6 +393,10 @@ void MaterialParameter::Bind(BindMeta& meta) const
case MaterialSceneTextures::Specular:
view = meta.CanSampleGBuffer ? meta.Buffers->GBuffer2->View() : nullptr;
break;
case MaterialSceneTextures::SceneStencil:
case MaterialSceneTextures::ObjectLayer:
view = meta.CanSampleDepth ? meta.Buffers->DepthBuffer->ViewStencil() : nullptr;
break;
default: ;
}
}
@@ -639,6 +644,7 @@ void MaterialParams::Dispose()
bool MaterialParams::Load(ReadStream* stream)
{
PROFILE_MEM(GraphicsMaterials);
bool result = false;
// Release

View File

@@ -11,6 +11,7 @@
#include "Engine/Graphics/Shaders/GPUConstantBuffer.h"
#include "Engine/Graphics/Shaders/GPUShader.h"
#include "Engine/Engine/Time.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "DecalMaterialShader.h"
#include "PostFxMaterialShader.h"
#include "ForwardMaterialShader.h"
@@ -37,14 +38,14 @@ GPU_CB_STRUCT(MaterialShaderDataPerView {
Float3 LargeWorldsChunkIndex;
float LargeWorldsChunkSize;
Float3 ViewPadding0;
float UnscaledTimeParam;
float ScaledTimeParam;
});
IMaterial::BindParameters::BindParameters(::GPUContext* context, const ::RenderContext& renderContext)
: GPUContext(context)
, RenderContext(renderContext)
, Time(Time::Draw.Time.GetTotalSeconds())
, UnscaledTime(Time::Draw.UnscaledTime.GetTotalSeconds())
, Time(Time::Draw.UnscaledTime.GetTotalSeconds())
, ScaledTime(Time::Draw.Time.GetTotalSeconds())
{
}
@@ -52,8 +53,8 @@ IMaterial::BindParameters::BindParameters(::GPUContext* context, const ::RenderC
: GPUContext(context)
, RenderContext(renderContext)
, DrawCall(&drawCall)
, Time(Time::Draw.Time.GetTotalSeconds())
, UnscaledTime(Time::Draw.UnscaledTime.GetTotalSeconds())
, Time(Time::Draw.UnscaledTime.GetTotalSeconds())
, ScaledTime(Time::Draw.Time.GetTotalSeconds())
, Instanced(instanced)
{
}
@@ -82,7 +83,7 @@ void IMaterial::BindParameters::BindViewData()
cb.ViewFar = view.Far;
cb.ViewDir = view.Direction;
cb.TimeParam = Time;
cb.UnscaledTimeParam = UnscaledTime;
cb.ScaledTimeParam = ScaledTime;
cb.ViewInfo = view.ViewInfo;
cb.ScreenSize = view.ScreenSize;
cb.TemporalAAJitter = view.TemporalAAJitter;
@@ -141,6 +142,7 @@ MaterialShader::~MaterialShader()
MaterialShader* MaterialShader::Create(const StringView& name, MemoryReadStream& shaderCacheStream, const MaterialInfo& info)
{
PROFILE_MEM(GraphicsMaterials);
MaterialShader* material;
switch (info.Domain)
{
@@ -204,6 +206,7 @@ protected:
MaterialShader* MaterialShader::CreateDummy(MemoryReadStream& shaderCacheStream, const MaterialInfo& info)
{
PROFILE_MEM(GraphicsMaterials);
MaterialShader* material = New<DummyMaterial>();
if (material->Load(shaderCacheStream, info))
{
@@ -230,6 +233,7 @@ bool MaterialShader::IsReady() const
bool MaterialShader::Load(MemoryReadStream& shaderCacheStream, const MaterialInfo& info)
{
PROFILE_MEM(GraphicsMaterials);
ASSERT(!_isLoaded);
// Cache material info

View File

@@ -10,7 +10,7 @@
/// <summary>
/// Current materials shader version.
/// </summary>
#define MATERIAL_GRAPH_VERSION 174
#define MATERIAL_GRAPH_VERSION 178
class Material;
class GPUShader;

View File

@@ -2,6 +2,7 @@
#include "MaterialShaderFeatures.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/RenderBuffers.h"
#include "Engine/Graphics/Textures/GPUTexture.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Renderer/ShadowsPass.h"
@@ -24,18 +25,30 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, Span<by
const int32 skyLightShaderRegisterIndex = srv + 1;
const int32 shadowsBufferRegisterIndex = srv + 2;
const int32 shadowMapShaderRegisterIndex = srv + 3;
const int32 volumetricFogTextureRegisterIndex = srv + 4;
const bool canUseShadow = view.Pass != DrawPass::Depth;
// Set fog input
GPUTextureView* volumetricFogTexture = nullptr;
if (cache->Fog)
{
cache->Fog->GetExponentialHeightFogData(view, data.ExponentialHeightFog);
VolumetricFogOptions volumetricFog;
cache->Fog->GetVolumetricFogOptions(volumetricFog);
if (volumetricFog.UseVolumetricFog() && params.RenderContext.Buffers->VolumetricFog)
volumetricFogTexture = params.RenderContext.Buffers->VolumetricFog->ViewVolume();
else
data.ExponentialHeightFog.VolumetricFogMaxDistance = -1.0f;
}
else
{
data.ExponentialHeightFog.FogMinOpacity = 1.0f;
data.ExponentialHeightFog.FogDensity = 0.0f;
data.ExponentialHeightFog.FogCutoffDistance = 0.1f;
data.ExponentialHeightFog.StartDistance = 0.0f;
data.ExponentialHeightFog.ApplyDirectionalInscattering = 0.0f;
}
params.GPUContext->BindSR(volumetricFogTextureRegisterIndex, volumetricFogTexture);
// Set directional light input
if (cache->DirectionalLights.HasItems())

View File

@@ -25,7 +25,7 @@ struct ForwardShadingFeature : MaterialShaderFeature
{
enum { MaxLocalLights = 4 };
enum { SRVs = 4 };
enum { SRVs = 5 };
PACK_STRUCT(struct Data
{

View File

@@ -49,7 +49,6 @@ void ParticleMaterialShader::Bind(BindParameters& params)
auto context = params.GPUContext;
auto& view = params.RenderContext.View;
auto& drawCall = *params.DrawCall;
const uint32 sortedIndicesOffset = drawCall.Particle.Module->SortedIndicesOffset;
Span<byte> cb(_cbData.Get(), _cbData.Count());
ASSERT_LOW_LAYER(cb.Length() >= sizeof(ParticleMaterialShaderData));
auto materialData = reinterpret_cast<ParticleMaterialShaderData*>(cb.Get());
@@ -103,7 +102,7 @@ void ParticleMaterialShader::Bind(BindParameters& params)
static StringView ParticleModelFacingModeOffset(TEXT("ModelFacingMode"));
materialData->WorldMatrix.SetMatrixTranspose(drawCall.World);
materialData->SortedIndicesOffset = drawCall.Particle.Particles->GPU.SortedIndices && params.RenderContext.View.Pass != DrawPass::Depth ? sortedIndicesOffset : 0xFFFFFFFF;
materialData->SortedIndicesOffset = drawCall.Particle.Particles->GPU.SortedIndices && params.RenderContext.View.Pass != DrawPass::Depth ? drawCall.Particle.Module->SortedIndicesOffset : 0xFFFFFFFF;
materialData->PerInstanceRandom = drawCall.PerInstanceRandom;
materialData->ParticleStride = drawCall.Particle.Particles->Stride;
materialData->PositionOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticlePosition, ParticleAttribute::ValueTypes::Float3);
@@ -265,5 +264,10 @@ bool ParticleMaterialShader::Load()
// Lazy initialization
_cacheVolumetricFog.Desc.PS = nullptr;
#if PLATFORM_PS5
// Fix shader binding issues on forward shading materials on PS5
_drawModes = DrawPass::None;
#endif
return false;
}

View File

@@ -22,7 +22,7 @@ PACK_STRUCT(struct PostFxMaterialShaderData {
Float4 TemporalAAJitter;
Matrix InverseViewProjectionMatrix;
Float3 ViewPadding0;
float UnscaledTimeParam;
float ScaledTimeParam;
});
void PostFxMaterialShader::Bind(BindParameters& params)
@@ -54,7 +54,7 @@ void PostFxMaterialShader::Bind(BindParameters& params)
materialData->ViewFar = view.Far;
materialData->ViewDir = view.Direction;
materialData->TimeParam = params.Time;
materialData->UnscaledTimeParam = params.UnscaledTime;
materialData->ScaledTimeParam = params.ScaledTime;
materialData->ViewInfo = view.ViewInfo;
materialData->ScreenSize = view.ScreenSize;
materialData->TemporalAAJitter = view.TemporalAAJitter;

View File

@@ -76,7 +76,7 @@ void TerrainMaterialShader::Bind(BindParameters& params)
scaleX > 0.00001f ? 1.0f / scaleX : 0.0f,
scaleY > 0.00001f ? 1.0f / scaleY : 0.0f,
scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f);
materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign;
materialData->WorldDeterminantSign = drawCall.WorldDeterminant ? -1.0f : 1.0f;
materialData->PerInstanceRandom = drawCall.PerInstanceRandom;
materialData->CurrentLOD = drawCall.Terrain.CurrentLOD;
materialData->ChunkSizeNextLOD = drawCall.Terrain.ChunkSizeNextLOD;
@@ -109,13 +109,10 @@ void TerrainMaterialShader::Bind(BindParameters& params)
if (IsRunningRadiancePass)
cullMode = CullMode::TwoSided;
#endif
if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0)
if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminant)
{
// Invert culling when scale is negative
if (cullMode == CullMode::Normal)
cullMode = CullMode::Inverted;
else
cullMode = CullMode::Normal;
cullMode = cullMode == CullMode::Normal ? CullMode::Inverted : CullMode::Normal;
}
const PipelineStateCache* psCache = _cache.GetPS(view.Pass, useLightmap);
ASSERT(psCache);
@@ -123,6 +120,7 @@ void TerrainMaterialShader::Bind(BindParameters& params)
// Bind pipeline
context->SetState(state);
context->SetStencilRef(drawCall.StencilValue);
}
void TerrainMaterialShader::Unload()
@@ -139,6 +137,10 @@ bool TerrainMaterialShader::Load()
psDesc.DepthEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthTest) == MaterialFeaturesFlags::None;
psDesc.DepthWriteEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthWrite) == MaterialFeaturesFlags::None;
psDesc.StencilEnable = true;
psDesc.StencilReadMask = 0;
psDesc.StencilPassOp = StencilOperation::Replace;
#if GPU_ALLOW_TESSELLATION_SHADERS
// Check if use tessellation (both material and runtime supports it)
const bool useTess = _info.TessellationMode != TessellationMethod::None && GPUDevice::Instance->Limits.HasTessellation;

View File

@@ -14,6 +14,7 @@
#include "Engine/Renderer/RenderList.h"
#include "Engine/Scripting/ManagedCLR/MCore.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Profiler/ProfilerMemory.h"
#if USE_EDITOR
#include "Engine/Renderer/GBufferPass.h"
#endif
@@ -48,6 +49,7 @@ namespace
{
bool UpdateMesh(MeshBase* mesh, uint32 vertexCount, uint32 triangleCount, PixelFormat indexFormat, const Float3* vertices, const void* triangles, const Float3* normals, const Float3* tangents, const Float2* uvs, const Color32* colors)
{
PROFILE_MEM(GraphicsMeshes);
auto model = mesh->GetModelBase();
CHECK_RETURN(model && model->IsVirtual(), true);
CHECK_RETURN(triangles && vertices, true);
@@ -172,6 +174,7 @@ bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0Element
bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0ElementType* vb0, const VB1ElementType* vb1, const VB2ElementType* vb2, const void* ib, bool use16BitIndices)
{
PROFILE_MEM(GraphicsMeshes);
Release();
// Setup GPU resources
@@ -215,7 +218,7 @@ bool Mesh::Load(uint32 vertices, uint32 triangles, const void* vb0, const void*
return Init(vertices, triangles, vbData, ib, use16BitIndexBuffer, vbLayout);
}
void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom, int8 sortOrder) const
void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom, int8 sortOrder, uint8 stencilValue) const
{
if (!material || !material->IsSurface() || !IsInitialized())
return;
@@ -237,8 +240,8 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons
drawCall.ObjectRadius = (float)_sphere.Radius * drawCall.World.GetScaleVector().GetAbsolute().MaxValue();
drawCall.Surface.GeometrySize = _box.GetSize();
drawCall.Surface.PrevWorld = world;
drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
drawCall.PerInstanceRandom = perInstanceRandom;
drawCall.StencilValue = stencilValue;
#if USE_EDITOR
const ViewMode viewMode = renderContext.View.Mode;
if (viewMode == ViewMode::LightmapUVsDensity || viewMode == ViewMode::LODPreview)
@@ -304,8 +307,8 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float
drawCall.Surface.Lightmap = (info.Flags & StaticFlags::Lightmap) != StaticFlags::None ? info.Lightmap : nullptr;
drawCall.Surface.LightmapUVsArea = info.LightmapUVs ? *info.LightmapUVs : Rectangle::Empty;
drawCall.Surface.LODDitherFactor = lodDitherFactor;
drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
drawCall.StencilValue = info.StencilValue;
#if USE_EDITOR
const ViewMode viewMode = renderContext.View.Mode;
if (viewMode == ViewMode::LightmapUVsDensity || viewMode == ViewMode::LODPreview)
@@ -367,8 +370,8 @@ void Mesh::Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& in
drawCall.Surface.Lightmap = (info.Flags & StaticFlags::Lightmap) != StaticFlags::None ? info.Lightmap : nullptr;
drawCall.Surface.LightmapUVsArea = info.LightmapUVs ? *info.LightmapUVs : Rectangle::Empty;
drawCall.Surface.LODDitherFactor = lodDitherFactor;
drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
drawCall.StencilValue = info.StencilValue;
#if USE_EDITOR
const ViewMode viewMode = renderContextBatch.GetMainContext().View.Mode;
if (viewMode == ViewMode::LightmapUVsDensity || viewMode == ViewMode::LODPreview)

View File

@@ -142,7 +142,8 @@ public:
/// <param name="drawModes">The draw passes to use for rendering this object.</param>
/// <param name="perInstanceRandom">The random per-instance value (normalized to range 0-1).</param>
/// <param name="sortOrder">Object sorting key.</param>
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f, int8 sortOrder = 0) const;
/// <param name="stencilValue">Object stencil value.</param>
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f, int8 sortOrder = 0, uint8 stencilValue = 0) const;
/// <summary>
/// Draws the mesh.

View File

@@ -16,6 +16,7 @@
#include "Engine/Scripting/ManagedCLR/MCore.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Threading/Task.h"
#include "Engine/Threading/Threading.h"
static_assert(MODEL_MAX_VB == 3, "Update code in mesh to match amount of vertex buffers.");
@@ -443,7 +444,7 @@ bool MeshBase::Init(uint32 vertices, uint32 triangles, const Array<const void*,
// Create GPU buffers
#if GPU_ENABLE_RESOURCE_NAMING
const String& modelPath = _model->GetPath();
const String modelPath = _model->GetPath();
#define MESH_BUFFER_NAME(postfix) modelPath + TEXT(postfix)
#else
#define MESH_BUFFER_NAME(postfix) String::Empty

View File

@@ -404,6 +404,11 @@ public:
/// </summary>
float PerInstanceRandom;
/// <summary>
/// The 8-bit stencil value to write into Depth-Stencil Buffer.
/// </summary>
uint8 StencilValue;
/// <summary>
/// The LOD bias value.
/// </summary>
@@ -422,6 +427,12 @@ public:
#if USE_EDITOR
float LightmapScale = -1.0f;
#endif
// Packs object layer into the stencil bits.
FORCE_INLINE void SetStencilValue(int32 layer)
{
StencilValue = uint8(layer & 0x1f);
}
};
/// <summary>

View File

@@ -372,6 +372,8 @@ bool MaterialSlotEntry::UsesProperties() const
Opacity.TextureIndex != -1 ||
Math::NotNearEqual(Roughness.Value, 0.5f) ||
Roughness.TextureIndex != -1 ||
Math::NotNearEqual(Metalness.Value, 0.5f) ||
Metalness.TextureIndex != -1 ||
Normals.TextureIndex != -1;
}

View File

@@ -327,14 +327,23 @@ struct FLAXENGINE_API MaterialSlotEntry
{
float Value = 0.5f;
int32 TextureIndex = -1;
uint8 Channel = 0;
} Roughness;
struct
{
float Value = 0.0f;
int32 TextureIndex = -1;
uint8 Channel = 0;
} Metalness;
struct
{
int32 TextureIndex = -1;
} Normals;
bool TwoSided = false;
bool Wireframe = false;
bool UsesProperties() const;
static float ShininessToRoughness(float shininess);
@@ -434,7 +443,8 @@ public:
{
Float32,
Float16,
} PositionFormat = PositionFormats::Float32;
Automatic,
} PositionFormat = PositionFormats::Automatic;
// See ModelTool::TexCoordFormats
enum class TexCoordFormats

View File

@@ -4,6 +4,7 @@
#include "Engine/Serialization/Serialization.h"
#include "Engine/Content/Assets/Model.h"
#include "Engine/Content/Assets/SkinnedModel.h"
#include "Engine/Profiler/ProfilerMemory.h"
bool ModelInstanceEntries::HasContentLoaded() const
{
@@ -41,6 +42,7 @@ void ModelInstanceEntries::Serialize(SerializeStream& stream, const void* otherO
void ModelInstanceEntries::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
{
PROFILE_MEM(Graphics);
const DeserializeStream& entries = stream["Entries"];
ASSERT(entries.IsArray());
Resize(entries.Size());
@@ -85,6 +87,7 @@ void ModelInstanceEntries::Setup(const SkinnedModel* model)
void ModelInstanceEntries::Setup(int32 slotsCount)
{
PROFILE_MEM(Graphics);
Clear();
Resize(slotsCount);
}

View File

@@ -158,6 +158,7 @@ void SkeletonData::Swap(SkeletonData& other)
Transform SkeletonData::GetNodeTransform(int32 nodeIndex) const
{
CHECK_RETURN(Nodes.IsValidIndex(nodeIndex), Transform::Identity);
const int32 parentIndex = Nodes[nodeIndex].ParentIndex;
if (parentIndex == -1)
{
@@ -169,6 +170,7 @@ Transform SkeletonData::GetNodeTransform(int32 nodeIndex) const
void SkeletonData::SetNodeTransform(int32 nodeIndex, const Transform& value)
{
CHECK(Nodes.IsValidIndex(nodeIndex));
const int32 parentIndex = Nodes[nodeIndex].ParentIndex;
if (parentIndex == -1)
{
@@ -312,8 +314,8 @@ void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info,
drawCall.Surface.PrevWorld = info.DrawState->PrevWorld;
drawCall.Surface.Skinning = info.Skinning;
drawCall.Surface.LODDitherFactor = lodDitherFactor;
drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
drawCall.StencilValue = info.StencilValue;
// Push draw call to the render list
renderContext.List->AddDrawCall(renderContext, drawModes, StaticFlags::None, drawCall, entry.ReceiveDecals, info.SortOrder);
@@ -353,8 +355,8 @@ void SkinnedMesh::Draw(const RenderContextBatch& renderContextBatch, const DrawI
drawCall.Surface.PrevWorld = info.DrawState->PrevWorld;
drawCall.Surface.Skinning = info.Skinning;
drawCall.Surface.LODDitherFactor = lodDitherFactor;
drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
drawCall.StencilValue = info.StencilValue;
// Push draw call to the render lists
const auto shadowsMode = entry.ShadowsMode & slot.ShadowsMode;

View File

@@ -38,29 +38,6 @@ void SkinnedMeshDrawData::Setup(int32 bonesCount)
SAFE_DELETE_GPU_RESOURCE(PrevBoneMatrices);
}
void SkinnedMeshDrawData::SetData(const Matrix* bones, bool dropHistory)
{
if (!bones)
return;
ANIM_GRAPH_PROFILE_EVENT("SetSkinnedMeshData");
// Copy bones to the buffer
const int32 count = BonesCount;
const int32 preFetchStride = 2;
const Matrix* input = bones;
const auto output = (Matrix3x4*)Data.Get();
ASSERT(Data.Count() == count * sizeof(Matrix3x4));
for (int32 i = 0; i < count; i++)
{
Matrix3x4* bone = output + i;
Platform::Prefetch(bone + preFetchStride);
Platform::Prefetch((byte*)(bone + preFetchStride) + PLATFORM_CACHE_LINE_SIZE);
bone->SetMatrixTranspose(input[i]);
}
OnDataChanged(dropHistory);
}
void SkinnedMeshDrawData::OnDataChanged(bool dropHistory)
{
// Setup previous frame bone matrices if needed

View File

@@ -69,13 +69,6 @@ public:
/// <param name="bonesCount">The bones count.</param>
void Setup(int32 bonesCount);
/// <summary>
/// Sets the bone matrices data for the GPU buffer. Ensure to call Flush before rendering.
/// </summary>
/// <param name="bones">The bones data.</param>
/// <param name="dropHistory">True if drop previous update bones used for motion blur, otherwise will keep them and do the update.</param>
void SetData(const Matrix* bones, bool dropHistory);
/// <summary>
/// After bones Data has been modified externally. Updates the bone matrices data for the GPU buffer. Ensure to call Flush before rendering.
/// </summary>

View File

@@ -113,7 +113,8 @@ GPUTexture* RenderBuffers::RequestHalfResDepth(GPUContext* context)
PixelFormat RenderBuffers::GetOutputFormat() const
{
return _useAlpha ? PixelFormat::R16G16B16A16_Float : PixelFormat::R11G11B10_Float;
// TODO: fix incorrect alpha leaking into reflections on PS5 with R11G11B10_Float
return _useAlpha || PLATFORM_PS5 ? PixelFormat::R16G16B16A16_Float : PixelFormat::R11G11B10_Float;
}
bool RenderBuffers::GetUseAlpha() const

View File

@@ -13,6 +13,17 @@
#define GBUFFER2_FORMAT PixelFormat::R8G8B8A8_UNorm
#define GBUFFER3_FORMAT PixelFormat::R8G8B8A8_UNorm
// Stencil bits usage (must match GBuffer.hlsl)
// [0] | Object Layer
// [1] | Object Layer
// [2] | Object Layer
// [3] | Object Layer
// [4] | Object Layer
// [5] | <unsued>
// [6] | <unsued>
// [7] | <unsued>
#define STENCIL_BUFFER_OBJECT_LAYER(value) uint8(value & 0x1f)
/// <summary>
/// The scene rendering buffers container.
/// </summary>

View File

@@ -8,6 +8,7 @@
#include "Engine/Core/Log.h"
#include "Engine/Engine/Engine.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Threading/Threading.h"
struct Entry
{

View File

@@ -200,12 +200,19 @@ void SceneRenderTask::RemoveGlobalCustomPostFx(PostProcessEffect* fx)
void SceneRenderTask::CollectPostFxVolumes(RenderContext& renderContext)
{
PROFILE_CPU();
// Cache WorldPosition used for PostFx volumes blending (RenderView caches it later on)
renderContext.View.WorldPosition = renderContext.View.Origin + renderContext.View.Position;
if (EnumHasAllFlags(ActorsSource, ActorsSources::Scenes))
{
Level::CollectPostFxVolumes(renderContext);
//ScopeLock lock(Level::ScenesLock);
for (Scene* scene : Level::Scenes)
{
if (scene->IsActiveInHierarchy())
scene->Rendering.CollectPostFxVolumes(renderContext);
}
}
if (EnumHasAllFlags(ActorsSource, ActorsSources::CustomActors))
{
@@ -417,6 +424,7 @@ void SceneRenderTask::OnEnd(GPUContext* context)
bool SceneRenderTask::Resize(int32 width, int32 height)
{
PROFILE_MEM(Graphics);
if (Output && Output->Resize(width, height))
return true;
if (Buffers && Buffers->Init((int32)((float)width * RenderingPercentage), (int32)((float)height * RenderingPercentage)))

View File

@@ -513,7 +513,7 @@ API_STRUCT(NoDefault) struct RenderContextBatch
/// <summary>
/// The Job System labels to wait on, after draw calls collecting.
/// </summary>
API_FIELD() Array<uint64, InlinedAllocation<8>> WaitLabels;
API_FIELD() Array<int64, InlinedAllocation<8>> WaitLabels;
/// <summary>
/// Enables using async tasks via Job System when performing drawing.

View File

@@ -265,6 +265,14 @@ public:
return Projection.M44 >= 1.0f;
}
/// <summary>
/// Determines whether view Origin has been moved in this frame. Old history buffers/data might be invalid.
/// </summary>
FORCE_INLINE bool IsOriginTeleport() const
{
return Origin != PrevOrigin;
}
public:
// Ignore deprecation warnings in defaults
PRAGMA_DISABLE_DEPRECATION_WARNINGS

View File

@@ -217,7 +217,7 @@ bool ShaderAssetBase::LoadShaderCache(ShaderCacheResult& result)
&& parent->HasChunk(SHADER_FILE_CHUNK_SOURCE))
{
result.Data.Release();
const String parentPath = parent->GetPath();
const StringView parentPath = parent->GetPath();
const Guid parentID = parent->GetID();
LOG(Info, "Compiling shader '{0}':{1}...", parentPath, parentID);

View File

@@ -8,6 +8,7 @@
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/Shaders/GPUVertexLayout.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Profiler/ProfilerMemory.h"
static FORCE_INLINE uint32 HashPermutation(const StringAnsiView& name, int32 permutationIndex)
{
@@ -24,6 +25,25 @@ void GPUShaderProgram::Init(const GPUShaderProgramInitializer& initializer)
#endif
}
#if !BUILD_RELEASE
void GPUShaderProgram::GetDebugName(DebugName& name) const
{
StringView ownerName = StringUtils::GetFileNameWithoutExtension(_owner->GetName());
name.AddUninitialized(ownerName.Length() + _name.Length() + 2);
char* dst = name.Get();
for (int32 i = 0; i < ownerName.Length(); i++)
dst[i] = (char)ownerName.Get()[i];
dst += ownerName.Length();
*dst = ':';
dst++;
for (int32 i = 0; i < _name.Length(); i++)
dst[i] = _name.Get()[i];
dst[_name.Length()] = 0;
}
#endif
GPUShader::GPUShader()
: GPUResource(SpawnParams(Guid::New(), TypeInitializer))
{
@@ -33,6 +53,7 @@ GPUShader::GPUShader()
bool GPUShader::Create(MemoryReadStream& stream)
{
ReleaseGPU();
_memoryUsage = sizeof(GPUShader);
// Version
int32 version;
@@ -111,6 +132,7 @@ bool GPUShader::Create(MemoryReadStream& stream)
const uint32 hash = HashPermutation(shader->GetName(), permutationIndex);
ASSERT_LOW_LAYER(!_shaders.ContainsKey(hash));
_shaders.Add(hash, shader);
_memoryUsage += sizeof(GPUShaderProgram) + bytecodeSize;
}
}
@@ -142,11 +164,12 @@ bool GPUShader::Create(MemoryReadStream& stream)
return true;
}
_constantBuffers[slotIndex] = cb;
_memoryUsage += sizeof(GPUConstantBuffer);
}
// Don't read additional data
_memoryUsage = 1;
PROFILE_MEM_INC(GraphicsShaders, _memoryUsage);
return false;
}
@@ -208,6 +231,7 @@ GPUResourceType GPUShader::GetResourceType() const
void GPUShader::OnReleaseGPU()
{
PROFILE_MEM_DEC(GraphicsShaders, _memoryUsage);
for (GPUConstantBuffer*& cb : _constantBuffers)
{
if (cb)

View File

@@ -4,6 +4,9 @@
#include "Engine/Core/Types/BaseTypes.h"
#include "Engine/Core/Types/String.h"
#if !BUILD_RELEASE
#include "Engine/Core/Collections/Array.h"
#endif
#include "Config.h"
class GPUShader;
@@ -93,6 +96,11 @@ public:
return _flags;
}
#if !BUILD_RELEASE
typedef Array<char, InlinedAllocation<60>> DebugName;
void GetDebugName(DebugName& name) const;
#endif
public:
/// <summary>
/// Gets shader program stage type.

View File

@@ -216,20 +216,21 @@ GPUVertexLayout* GPUVertexLayout::Get(const Span<GPUVertexLayout*>& layouts)
return result;
}
GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused, bool addMissing, int32 missingSlotOverride)
GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused, bool addMissing, int32 missingSlotOverride, bool referenceOrder)
{
GPUVertexLayout* result = base ? base : reference;
if (base && reference && base != reference)
{
bool elementsModified = false;
Elements newElements = base->GetElements();
const Elements& refElements = reference->GetElements();
if (removeUnused)
{
for (int32 i = newElements.Count() - 1; i >= 0; i--)
{
bool missing = true;
const VertexElement& e = newElements.Get()[i];
for (const VertexElement& ee : reference->GetElements())
for (const VertexElement& ee : refElements)
{
if (ee.Type == e.Type)
{
@@ -247,7 +248,7 @@ GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout*
}
if (addMissing)
{
for (const VertexElement& e : reference->GetElements())
for (const VertexElement& e : refElements)
{
bool missing = true;
for (const VertexElement& ee : base->GetElements())
@@ -282,6 +283,32 @@ GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout*
}
}
}
if (referenceOrder)
{
for (int32 i = 0, j = 0; i < newElements.Count() && j < refElements.Count(); j++)
{
if (newElements[i].Type == refElements[j].Type)
{
// Elements match so move forward
i++;
continue;
}
// Find reference element in a new list
for (int32 k = i + 1; k < newElements.Count(); k++)
{
if (newElements[k].Type == refElements[j].Type)
{
// Move matching element to the reference position
VertexElement e = newElements[k];
newElements.RemoveAt(k);
newElements.Insert(i, e);
i++;
break;
}
}
}
}
if (elementsModified)
result = Get(newElements, true);
}

View File

@@ -84,8 +84,9 @@ public:
/// <param name="removeUnused">True to remove elements from base layout that don't exist in a reference layout.</param>
/// <param name="addMissing">True to add missing elements to base layout that exist in a reference layout.</param>
/// <param name="missingSlotOverride">Allows to override the input slot for missing elements. Use value -1 to inherit slot from the reference layout.</param>
/// <param name="referenceOrder">True to reorder result elements to match the reference layout. For example, if input vertex buffer layout is different than vertex shader then it can match those.</param>
/// <returns>Vertex layout object. Doesn't need to be cleared as it's cached for an application lifetime.</returns>
static GPUVertexLayout* Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused = false, bool addMissing = true, int32 missingSlotOverride = -1);
static GPUVertexLayout* Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused = false, bool addMissing = true, int32 missingSlotOverride = -1, bool referenceOrder = false);
public:
// [GPUResource]

View File

@@ -16,6 +16,7 @@
#include "Engine/Threading/ThreadPoolTask.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/Enums.h"
namespace
@@ -353,6 +354,8 @@ int32 GPUTexture::ComputeRowPitch(int32 mipLevel, int32 rowAlign) const
bool GPUTexture::Init(const GPUTextureDescription& desc)
{
PROFILE_MEM(GraphicsTextures);
// Validate description
const auto device = GPUDevice::Instance;
if (desc.Usage == GPUResourceUsage::Dynamic)
@@ -501,6 +504,17 @@ bool GPUTexture::Init(const GPUTextureDescription& desc)
return true;
}
#if COMPILE_WITH_PROFILER
auto group = ProfilerMemory::Groups::GraphicsTextures;
if (_desc.IsRenderTarget())
group = ProfilerMemory::Groups::GraphicsRenderTargets;
else if (_desc.IsCubeMap())
group = ProfilerMemory::Groups::GraphicsCubeMaps;
else if (_desc.IsVolume())
group = ProfilerMemory::Groups::GraphicsVolumeTextures;
ProfilerMemory::IncrementGroup(group, _memoryUsage);
#endif
// Render targets and depth buffers doesn't support normal textures streaming and are considered to be always resident
if (IsRegularTexture() == false)
{
@@ -589,6 +603,17 @@ GPUResourceType GPUTexture::GetResourceType() const
void GPUTexture::OnReleaseGPU()
{
#if COMPILE_WITH_PROFILER
auto group = ProfilerMemory::Groups::GraphicsTextures;
if (_desc.IsRenderTarget())
group = ProfilerMemory::Groups::GraphicsRenderTargets;
else if (_desc.IsCubeMap())
group = ProfilerMemory::Groups::GraphicsCubeMaps;
else if (_desc.IsVolume())
group = ProfilerMemory::Groups::GraphicsVolumeTextures;
ProfilerMemory::DecrementGroup(group, _memoryUsage);
#endif
_desc.Clear();
_residentMipLevels = 0;
}
@@ -603,6 +628,7 @@ GPUTask* GPUTexture::UploadMipMapAsync(const BytesContainer& data, int32 mipInde
GPUTask* GPUTexture::UploadMipMapAsync(const BytesContainer& data, int32 mipIndex, int32 rowPitch, int32 slicePitch, bool copyData)
{
PROFILE_CPU();
PROFILE_MEM(GraphicsTextures);
ASSERT(IsAllocated());
ASSERT(mipIndex < MipLevels() && data.IsValid());
ASSERT(data.Length() >= slicePitch);
@@ -712,6 +738,7 @@ bool GPUTexture::DownloadData(TextureData& result)
MISSING_CODE("support volume texture data downloading.");
}
PROFILE_CPU();
PROFILE_MEM(GraphicsTextures);
// Use faster path for staging resources
if (IsStaging()) // TODO: what about chips with unified memory? if rendering is not active then we can access GPU memory from CPU directly (eg. mobile, integrated GPUs and some consoles)
@@ -798,6 +825,7 @@ Task* GPUTexture::DownloadDataAsync(TextureData& result)
MISSING_CODE("support volume texture data downloading.");
}
PROFILE_CPU();
PROFILE_MEM(GraphicsTextures);
// Use faster path for staging resources
if (IsStaging())

View File

@@ -437,6 +437,12 @@ public:
/// <returns>The view to the depth-stencil resource descriptor as read-only depth.</returns>
API_FUNCTION() virtual GPUTextureView* ViewReadOnlyDepth() const = 0;
/// <summary>
/// Gets the view to the texture as stencil buffer.
/// </summary>
/// <returns>The view to the stencil resource descriptor.</returns>
API_FUNCTION() virtual GPUTextureView* ViewStencil() const = 0;
/// <summary>
/// Implicit conversion to the first surface (only for 2D textures).
/// </summary>

View File

@@ -197,9 +197,22 @@ public:
~StreamTextureResizeTask()
{
OnResourceReleased2();
SAFE_DELETE_GPU_RESOURCE(_newTexture);
}
private:
void OnResourceReleased2()
{
// Unlink texture
if (_streamingTexture)
{
ScopeLock lock(_streamingTexture->GetOwner()->GetOwnerLocker());
_streamingTexture->_streamingTasks.Remove(this);
_streamingTexture = nullptr;
}
}
protected:
// [GPUTask]
Result run(GPUTasksContext* context) override
@@ -225,11 +238,7 @@ protected:
void OnEnd() override
{
if (_streamingTexture)
{
ScopeLock lock(_streamingTexture->GetOwner()->GetOwnerLocker());
_streamingTexture->_streamingTasks.Remove(this);
}
OnResourceReleased2();
// Base
GPUTask::OnEnd();
@@ -329,13 +338,18 @@ public:
StreamTextureMipTask(StreamingTexture* texture, int32 mipIndex, Task* rootTask)
: GPUUploadTextureMipTask(texture->GetTexture(), mipIndex, Span<byte>(nullptr, 0), 0, 0, false)
, _streamingTexture(texture)
, _rootTask(rootTask ? rootTask : this)
, _rootTask(rootTask)
, _dataLock(_streamingTexture->GetOwner()->LockData())
{
_streamingTexture->_streamingTasks.Add(_rootTask);
_streamingTexture->_streamingTasks.Add(this);
_texture.Released.Bind<StreamTextureMipTask, &StreamTextureMipTask::OnResourceReleased2>(this);
}
~StreamTextureMipTask()
{
OnResourceReleased2();
}
private:
void OnResourceReleased2()
{
@@ -343,7 +357,7 @@ private:
if (_streamingTexture)
{
ScopeLock lock(_streamingTexture->GetOwner()->GetOwnerLocker());
_streamingTexture->_streamingTasks.Remove(_rootTask);
_streamingTexture->_streamingTasks.Remove(this);
_streamingTexture = nullptr;
}
}
@@ -392,12 +406,7 @@ protected:
void OnEnd() override
{
_dataLock.Release();
if (_streamingTexture)
{
ScopeLock lock(_streamingTexture->GetOwner()->GetOwnerLocker());
_streamingTexture->_streamingTasks.Remove(_rootTask);
_streamingTexture = nullptr;
}
OnResourceReleased2();
// Base
GPUUploadTextureMipTask::OnEnd();
@@ -413,6 +422,15 @@ protected:
GPUUploadTextureMipTask::OnFail();
}
void OnCancel() override
{
GPUUploadTextureMipTask::OnCancel();
// Cancel the root task too (eg. mip loading from asset)
if (_rootTask != nullptr)
_rootTask->Cancel();
}
};
Task* StreamingTexture::CreateStreamingTask(int32 residency)

View File

@@ -771,7 +771,7 @@ Task* TextureBase::RequestMipDataAsync(int32 mipIndex)
FlaxStorage::LockData TextureBase::LockData()
{
return _parent->Storage ? _parent->Storage->Lock() : FlaxStorage::LockData::Invalid;
return _parent->Storage ? _parent->Storage->Lock() : FlaxStorage::LockData();
}
void TextureBase::GetMipData(int32 mipIndex, BytesContainer& data) const