Merge branch 'master' into PressGToGameModeAndPToNavigate
This commit is contained in:
@@ -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]);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
74
Source/Engine/Graphics/GPUPass.h
Normal file
74
Source/Engine/Graphics/GPUPass.h
Normal 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)
|
||||
@@ -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;
|
||||
|
||||
29
Source/Engine/Graphics/GPUResourceAccess.h
Normal file
29
Source/Engine/Graphics/GPUResourceAccess.h
Normal 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);
|
||||
@@ -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 />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -65,6 +65,7 @@ private:
|
||||
private:
|
||||
Cache _cache;
|
||||
Cache _cacheInstanced;
|
||||
bool _instanced;
|
||||
|
||||
public:
|
||||
DeferredMaterialShader(const StringView& name)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
/// <summary>
|
||||
/// Current materials shader version.
|
||||
/// </summary>
|
||||
#define MATERIAL_GRAPH_VERSION 174
|
||||
#define MATERIAL_GRAPH_VERSION 178
|
||||
|
||||
class Material;
|
||||
class GPUShader;
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -25,7 +25,7 @@ struct ForwardShadingFeature : MaterialShaderFeature
|
||||
{
|
||||
enum { MaxLocalLights = 4 };
|
||||
|
||||
enum { SRVs = 4 };
|
||||
enum { SRVs = 5 };
|
||||
|
||||
PACK_STRUCT(struct Data
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user