582 lines
20 KiB
C++
582 lines
20 KiB
C++
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "Engine/Graphics/GPUResource.h"
|
|
#include "GPUTextureDescription.h"
|
|
|
|
class GPUContext;
|
|
class GPUTask;
|
|
class TextureMipData;
|
|
class TextureData;
|
|
template<typename T>
|
|
class DataContainer;
|
|
typedef DataContainer<byte> BytesContainer;
|
|
class Task;
|
|
|
|
/// <summary>
|
|
/// Defines a view for the <see cref="GPUTexture"/> surface, full resource or any of the sub-parts. Can be used to define a single subresource of the texture, volume texture or texture array. Used to render to the texture and/or use textures in the shaders.
|
|
/// </summary>
|
|
API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API GPUTextureView : public GPUResourceView
|
|
{
|
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(GPUTextureView);
|
|
protected:
|
|
GPUResource* _parent = nullptr;
|
|
PixelFormat _format = PixelFormat::Unknown;
|
|
MSAALevel _msaa = MSAALevel::None;
|
|
|
|
GPUTextureView()
|
|
: GPUResourceView(SpawnParams(Guid::New(), TypeInitializer))
|
|
{
|
|
}
|
|
|
|
FORCE_INLINE void Init(GPUResource* parent, PixelFormat format, MSAALevel msaa)
|
|
{
|
|
_parent = parent;
|
|
_format = format;
|
|
_msaa = msaa;
|
|
if (parent)
|
|
LastRenderTime = &parent->LastRenderTime;
|
|
}
|
|
|
|
public:
|
|
/// <summary>
|
|
/// Gets parent GPU resource owning that view.
|
|
/// </summary>
|
|
API_PROPERTY() FORCE_INLINE GPUResource* GetParent() const
|
|
{
|
|
return _parent;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the view format.
|
|
/// </summary>
|
|
API_PROPERTY() FORCE_INLINE PixelFormat GetFormat() const
|
|
{
|
|
return _format;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets view MSAA level.
|
|
/// </summary>
|
|
API_PROPERTY() FORCE_INLINE MSAALevel GetMSAA() const
|
|
{
|
|
return _msaa;
|
|
}
|
|
};
|
|
|
|
/// <summary>
|
|
/// The GPU texture resource object. This class is able to create 2D/3D textures, volume textures and render targets.
|
|
/// </summary>
|
|
API_CLASS(Sealed) class FLAXENGINE_API GPUTexture : public GPUResource
|
|
{
|
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(GPUTexture);
|
|
static GPUTexture* Spawn(const SpawnParams& params);
|
|
static GPUTexture* New();
|
|
|
|
protected:
|
|
int32 _residentMipLevels;
|
|
bool _sRGB, _isBlockCompressed;
|
|
GPUTextureDescription _desc;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="GPUTexture"/> class.
|
|
/// </summary>
|
|
GPUTexture();
|
|
|
|
public:
|
|
/// <summary>
|
|
/// Gets a value indicating whether this texture has any resided mip (data already uploaded to the GPU).
|
|
/// </summary>
|
|
API_PROPERTY() FORCE_INLINE bool HasResidentMip() const
|
|
{
|
|
return _residentMipLevels != 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this texture has been allocated.
|
|
/// </summary>
|
|
API_PROPERTY() FORCE_INLINE bool IsAllocated() const
|
|
{
|
|
return _desc.MipLevels > 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets texture width (in texels).
|
|
/// </summary>
|
|
API_PROPERTY() FORCE_INLINE int32 Width() const
|
|
{
|
|
return _desc.Width;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets texture height (in texels).
|
|
/// </summary>
|
|
API_PROPERTY() FORCE_INLINE int32 Height() const
|
|
{
|
|
return _desc.Height;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets texture depth (in texels).
|
|
/// </summary>
|
|
API_PROPERTY() FORCE_INLINE int32 Depth() const
|
|
{
|
|
return _desc.Depth;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets number of textures in the array.
|
|
/// </summary>
|
|
API_PROPERTY() FORCE_INLINE int32 ArraySize() const
|
|
{
|
|
return _desc.ArraySize;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets multi-sampling parameters for the texture.
|
|
/// </summary>
|
|
API_PROPERTY() FORCE_INLINE MSAALevel MultiSampleLevel() const
|
|
{
|
|
return _desc.MultiSampleLevel;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets number of mipmap levels in the texture.
|
|
/// </summary>
|
|
API_PROPERTY() FORCE_INLINE int32 MipLevels() const
|
|
{
|
|
return _desc.MipLevels;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the number of resident mipmap levels in the texture (already uploaded to the GPU).
|
|
/// </summary>
|
|
API_PROPERTY() FORCE_INLINE int32 ResidentMipLevels() const
|
|
{
|
|
return _residentMipLevels;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the index of the highest resident mip map (may be equal to MipLevels if no mip has been uploaded). Note: mip=0 is the highest (top quality).
|
|
/// </summary>
|
|
API_PROPERTY() FORCE_INLINE int32 HighestResidentMipIndex() const
|
|
{
|
|
return MipLevels() - ResidentMipLevels();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets texture data format.
|
|
/// </summary>
|
|
API_PROPERTY() FORCE_INLINE PixelFormat Format() const
|
|
{
|
|
return _desc.Format;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets flags of the texture.
|
|
/// </summary>
|
|
API_PROPERTY() FORCE_INLINE GPUTextureFlags Flags() const
|
|
{
|
|
return _desc.Flags;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets texture dimensions.
|
|
/// </summary>
|
|
API_PROPERTY() FORCE_INLINE TextureDimensions Dimensions() const
|
|
{
|
|
return _desc.Dimensions;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets texture description structure.
|
|
/// </summary>
|
|
API_PROPERTY() FORCE_INLINE const GPUTextureDescription& GetDescription() const
|
|
{
|
|
return _desc;
|
|
}
|
|
|
|
public:
|
|
/// <summary>
|
|
/// Gets a value indicating whether this texture is a render target.
|
|
/// </summary>
|
|
FORCE_INLINE bool IsRenderTarget() const
|
|
{
|
|
return _desc.IsRenderTarget();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this texture is a shader resource.
|
|
/// </summary>
|
|
FORCE_INLINE bool IsShaderResource() const
|
|
{
|
|
return _desc.IsShaderResource();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this texture is a depth stencil.
|
|
/// </summary>
|
|
FORCE_INLINE bool IsDepthStencil() const
|
|
{
|
|
return _desc.IsDepthStencil();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this texture is a unordered access.
|
|
/// </summary>
|
|
FORCE_INLINE bool IsUnorderedAccess() const
|
|
{
|
|
return _desc.IsUnorderedAccess();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this instance has per mip level views.
|
|
/// </summary>
|
|
FORCE_INLINE bool HasPerMipViews() const
|
|
{
|
|
return _desc.HasPerMipViews();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this instance has per slice views.
|
|
/// </summary>
|
|
FORCE_INLINE bool HasPerSliceViews() const
|
|
{
|
|
return _desc.HasPerSliceViews();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this instance is a multi sample texture.
|
|
/// </summary>
|
|
FORCE_INLINE bool IsMultiSample() const
|
|
{
|
|
return _desc.IsMultiSample();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this instance is a cubemap texture.
|
|
/// </summary>
|
|
FORCE_INLINE bool IsCubeMap() const
|
|
{
|
|
return _desc.Dimensions == TextureDimensions::CubeTexture;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this instance is a volume texture.
|
|
/// </summary>
|
|
FORCE_INLINE bool IsVolume() const
|
|
{
|
|
return _desc.Dimensions == TextureDimensions::VolumeTexture;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this instance is an array texture.
|
|
/// </summary>
|
|
FORCE_INLINE bool IsArray() const
|
|
{
|
|
return _desc.ArraySize != 1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if texture contains sRGB colors data.
|
|
/// </summary>
|
|
FORCE_INLINE bool IsSRGB() const
|
|
{
|
|
return _sRGB;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if texture is normal texture asset (not render target or unordered access or depth buffer or sth else).
|
|
/// </summary>
|
|
FORCE_INLINE bool IsRegularTexture() const
|
|
{
|
|
return _desc.Flags == GPUTextureFlags::ShaderResource;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if texture is a staging buffer (supports direct CPU access).
|
|
/// </summary>
|
|
FORCE_INLINE bool IsStaging() const
|
|
{
|
|
return _desc.Usage == GPUResourceUsage::StagingUpload || _desc.Usage == GPUResourceUsage::StagingReadback;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a boolean indicating whether this <see cref="GPUTexture"/> is a using a block compress format (BC1, BC2, BC3, BC4, BC5, BC6H, BC7, etc.).
|
|
/// </summary>
|
|
FORCE_INLINE bool IsBlockCompressed() const
|
|
{
|
|
return _isBlockCompressed;
|
|
}
|
|
|
|
public:
|
|
/// <summary>
|
|
/// Gets the texture total size in pixels.
|
|
/// </summary>
|
|
API_PROPERTY() Float2 Size() const;
|
|
|
|
/// <summary>
|
|
/// Gets the texture total size in pixels (with depth).
|
|
/// </summary>
|
|
API_PROPERTY() Float3 Size3() const;
|
|
|
|
/// <summary>
|
|
/// Returns true if texture has size that is power of two.
|
|
/// </summary>
|
|
/// <returns>True if texture has size that is power of two.</returns>
|
|
API_PROPERTY() bool IsPowerOfTwo() const;
|
|
|
|
/// <summary>
|
|
/// Gets the texture mip map dimensions.
|
|
/// </summary>
|
|
/// <param name="mipLevelIndex">Mip level index (zero-based where 0 is top texture surface).</param>
|
|
/// <param name="mipWidth">The calculated mip level width (in pixels).</param>
|
|
/// <param name="mipHeight">The calculated mip level height (in pixels).</param>
|
|
API_FUNCTION() void GetMipSize(int32 mipLevelIndex, API_PARAM(Out) int32& mipWidth, API_PARAM(Out) int32& mipHeight) const;
|
|
|
|
/// <summary>
|
|
/// Gets the texture mip map dimensions.
|
|
/// </summary>
|
|
/// <param name="mipLevelIndex">Mip level index (zero-based where 0 is top texture surface).</param>
|
|
/// <param name="mipWidth">The calculated mip level width (in pixels).</param>
|
|
/// <param name="mipHeight">The calculated mip level height (in pixels).</param>
|
|
/// <param name="mipDepth">The calculated mip level depth (in pixels).</param>
|
|
API_FUNCTION() void GetMipSize(int32 mipLevelIndex, API_PARAM(Out) int32& mipWidth, API_PARAM(Out) int32& mipHeight, API_PARAM(Out) int32& mipDepth) const;
|
|
|
|
/// <summary>
|
|
/// Gets current texture size (uploaded to the GPU and in use).
|
|
/// </summary>
|
|
/// <param name="width">The current width (in pixels).</param>
|
|
/// <param name="height">The current height (in pixels).</param>
|
|
void GetResidentSize(int32& width, int32& height) const;
|
|
|
|
/// <summary>
|
|
/// Gets current texture size (uploaded to the GPU and in use).
|
|
/// </summary>
|
|
/// <param name="width">The current width (in pixels).</param>
|
|
/// <param name="height">The current height (in pixels).</param>
|
|
/// <param name="depth">The current depth (in pixels).</param>
|
|
void GetResidentSize(int32& width, int32& height, int32& depth) const;
|
|
|
|
public:
|
|
/// <summary>
|
|
/// Calculates mip map row pitch (in bytes).
|
|
/// </summary>
|
|
/// <param name="mipIndex">Index of the mip.</param>
|
|
/// <returns>Row pitch.</returns>
|
|
uint32 RowPitch(int32 mipIndex = 0) const;
|
|
|
|
/// <summary>
|
|
/// Calculates mip map slice pitch (in bytes).
|
|
/// </summary>
|
|
/// <param name="mipIndex">Index of the mip.</param>
|
|
/// <returns>Slice pitch.</returns>
|
|
uint32 SlicePitch(int32 mipIndex = 0) const;
|
|
|
|
/// <summary>
|
|
/// Computes row and slice pitch of the mip map.
|
|
/// </summary>
|
|
/// <param name="mipIndex">Index of the mip.</param>
|
|
/// <param name="rowPitch">The row pitch.</param>
|
|
/// <param name="slicePitch">The slice pitch.</param>
|
|
void ComputePitch(int32 mipIndex, uint32& rowPitch, uint32& slicePitch) const;
|
|
|
|
/// <summary>
|
|
/// Calculates the size of a particular mip.
|
|
/// </summary>
|
|
/// <param name="size">The size.</param>
|
|
/// <param name="mipLevel">The mip level.</param>
|
|
/// <returns>Mip size.</returns>
|
|
int32 CalculateMipSize(int32 size, int32 mipLevel) const;
|
|
|
|
public:
|
|
int32 ComputeSubresourceSize(int32 subresource, int32 rowAlign, int32 sliceAlign) const;
|
|
int32 ComputeBufferOffset(int32 subresource, int32 rowAlign, int32 sliceAlign) const;
|
|
int32 ComputeBufferTotalSize(int32 rowAlign, int32 sliceAlign) const;
|
|
int32 ComputeSlicePitch(int32 mipLevel, int32 rowAlign) const;
|
|
int32 ComputeRowPitch(int32 mipLevel, int32 rowAlign) const;
|
|
|
|
public:
|
|
/// <summary>
|
|
/// Gets the view to the first surface (only for 2D textures).
|
|
/// </summary>
|
|
/// <returns>The view to the main texture surface.</returns>
|
|
API_FUNCTION() FORCE_INLINE GPUTextureView* View() const
|
|
{
|
|
return View(0);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the view to the surface at index in an array.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// To use per depth/array slice view you need to specify the <see cref="GPUTextureFlags.PerSliceViews"/> when creating the resource.
|
|
/// </remarks>
|
|
/// <param name="arrayOrDepthIndex">The index of the surface in an array (or depth slice index).</param>
|
|
/// <returns>The view to the surface at index in an array.</returns>
|
|
API_FUNCTION() virtual GPUTextureView* View(int32 arrayOrDepthIndex) const = 0;
|
|
|
|
/// <summary>
|
|
/// Gets the view to the mip map surface at index in an array.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// To use per mip map view you need to specify the <see cref="GPUTextureFlags.PerMipViews"/> when creating the resource.
|
|
/// </remarks>
|
|
/// <param name="arrayOrDepthIndex">The index of the surface in an array (or depth slice index).</param>
|
|
/// <param name="mipMapIndex">Index of the mip level.</param>
|
|
/// <returns>The view to the surface at index in an array.</returns>
|
|
API_FUNCTION() virtual GPUTextureView* View(int32 arrayOrDepthIndex, int32 mipMapIndex) const = 0;
|
|
|
|
/// <summary>
|
|
/// Gets the view to the array of surfaces
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// To use array texture view you need to create render target as an array.
|
|
/// </remarks>
|
|
/// <returns>The view to the array of surfaces.</returns>
|
|
API_FUNCTION() virtual GPUTextureView* ViewArray() const = 0;
|
|
|
|
/// <summary>
|
|
/// Gets the view to the volume texture (3D).
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// To use volume texture view you need to create render target as a volume resource (3D texture with Depth > 1).
|
|
/// </remarks>
|
|
/// <returns>The view to the volume texture.</returns>
|
|
API_FUNCTION() virtual GPUTextureView* ViewVolume() const = 0;
|
|
|
|
/// <summary>
|
|
/// Gets the view to the texture as read-only depth/stencil buffer. Valid only if graphics device supports it and the texture uses depth/stencil.
|
|
/// </summary>
|
|
/// <returns>The view to the depth-stencil resource descriptor as read-only depth.</returns>
|
|
API_FUNCTION() virtual GPUTextureView* ViewReadOnlyDepth() const = 0;
|
|
|
|
/// <summary>
|
|
/// Implicit conversion to the first surface (only for 2D textures).
|
|
/// </summary>
|
|
/// <returns>The view to the main texture surface.</returns>
|
|
FORCE_INLINE operator GPUTextureView*() const
|
|
{
|
|
return View(0);
|
|
}
|
|
|
|
public:
|
|
/// <summary>
|
|
/// Initializes a texture resource (allocates the GPU memory and performs the resource setup).
|
|
/// </summary>
|
|
/// <param name="desc">The texture description.</param>
|
|
/// <returns>True if cannot create texture, otherwise false.</returns>
|
|
API_FUNCTION() bool Init(API_PARAM(Ref) const GPUTextureDescription& desc);
|
|
|
|
/// <summary>
|
|
/// Creates new staging readback texture with the same dimensions and properties as a source texture (but without a data transferred; warning: caller must delete object).
|
|
/// </summary>
|
|
/// <returns>The staging readback texture.</returns>
|
|
GPUTexture* ToStagingReadback() const;
|
|
|
|
/// <summary>
|
|
/// Creates new staging upload texture with the same dimensions and properties as a source texture (but without a data transferred; warning: caller must delete object).
|
|
/// </summary>
|
|
/// <returns>The staging upload texture.</returns>
|
|
GPUTexture* ToStagingUpload() const;
|
|
|
|
/// <summary>
|
|
/// Resizes the texture. It must be created first.
|
|
/// </summary>
|
|
/// <param name="width">The width.</param>
|
|
/// <param name="height">The height.</param>
|
|
/// <param name="format">The new texture format. Use Unknown to remain texture format unchanged.</param>
|
|
/// <returns>True if fails, otherwise false.</returns>
|
|
API_FUNCTION() bool Resize(int32 width, int32 height, PixelFormat format = PixelFormat::Unknown)
|
|
{
|
|
const auto depth = IsAllocated() ? Depth() : 1;
|
|
return Resize(width, height, depth, format);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resizes the texture. It must be created first.
|
|
/// </summary>
|
|
/// <param name="width">The width.</param>
|
|
/// <param name="height">The height.</param>
|
|
/// <param name="depth">The depth.</param>
|
|
/// <param name="format">The new texture format. Use Unknown to remain texture format unchanged.</param>
|
|
/// <returns>True if fails, otherwise false.</returns>
|
|
API_FUNCTION() bool Resize(int32 width, int32 height, int32 depth, PixelFormat format = PixelFormat::Unknown);
|
|
|
|
public:
|
|
/// <summary>
|
|
/// Gets the native pointer to the underlying resource. It's a low-level platform-specific handle.
|
|
/// </summary>
|
|
/// <returns>The pointer.</returns>
|
|
API_PROPERTY() virtual void* GetNativePtr() const = 0;
|
|
|
|
/// <summary>
|
|
/// Uploads mip map data to the GPU. Creates async GPU task.
|
|
/// </summary>
|
|
/// <param name="data">Data to upload (it must be valid for the next a few frames due to GPU latency and async works executing)</param>
|
|
/// <param name="mipIndex">Mip level index.</param>
|
|
/// <param name="copyData">If true, the data will be copied to the async execution task instead of using the input pointer provided.</param>
|
|
/// <returns>Created async task or null if cannot.</returns>
|
|
GPUTask* UploadMipMapAsync(const BytesContainer& data, int32 mipIndex, bool copyData = false);
|
|
|
|
/// <summary>
|
|
/// Uploads mip map data to the GPU. Creates async GPU task.
|
|
/// </summary>
|
|
/// <param name="data">Data to upload (it must be valid for the next a few frames due to GPU latency and async works executing)</param>
|
|
/// <param name="mipIndex">Mip level index.</param>
|
|
/// <param name="rowPitch">The data row pitch.</param>
|
|
/// <param name="slicePitch">The data slice pitch.</param>
|
|
/// <param name="copyData">If true, the data will be copied to the async execution task instead of using the input pointer provided.</param>
|
|
/// <returns>Created async task or null if cannot.</returns>
|
|
GPUTask* UploadMipMapAsync(const BytesContainer& data, int32 mipIndex, int32 rowPitch, int32 slicePitch, bool copyData = false);
|
|
|
|
/// <summary>
|
|
/// Stops current thread execution to gather texture data from the GPU.
|
|
/// </summary>
|
|
/// <param name="result">The result data.</param>
|
|
/// <returns>True if cannot download data, otherwise false.</returns>
|
|
bool DownloadData(TextureData& result);
|
|
|
|
/// <summary>
|
|
/// Creates GPU async task that will gather texture data from the GPU.
|
|
/// </summary>
|
|
/// <param name="result">The result data.</param>
|
|
/// <returns>Download data task (not started yet).</returns>
|
|
Task* DownloadDataAsync(TextureData& result);
|
|
|
|
/// <summary>
|
|
/// Gets texture mipmap data (raw bytes). Can be used only with textures created with Staging flag.
|
|
/// </summary>
|
|
/// <param name="arrayOrDepthSliceIndex">Array or depth slice index.</param>
|
|
/// <param name="mipMapIndex">Mip map index.</param>
|
|
/// <param name="data">Output mip data.</param>
|
|
/// <param name="mipRowPitch">Output mip data row pitch to use. Use 0 to use the pitch from the internal GPU storage.</param>
|
|
/// <returns>True if failed, otherwise false.</returns>
|
|
virtual bool GetData(int32 arrayOrDepthSliceIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch = 0) = 0;
|
|
|
|
/// <summary>
|
|
/// Sets the number of resident mipmap levels in the texture (already uploaded to the GPU).
|
|
/// </summary>
|
|
API_PROPERTY() void SetResidentMipLevels(int32 count);
|
|
|
|
/// <summary>
|
|
/// Event called when texture residency gets changed. Texture Mip gets loaded into GPU memory and is ready to use.
|
|
/// </summary>
|
|
Delegate<GPUTexture*> ResidentMipsChanged;
|
|
|
|
protected:
|
|
virtual bool OnInit() = 0;
|
|
uint64 calculateMemoryUsage() const;
|
|
virtual void OnResidentMipsChanged() = 0;
|
|
|
|
public:
|
|
// [GPUResource]
|
|
String ToString() const override;
|
|
GPUResourceType GetResourceType() const final override;
|
|
|
|
protected:
|
|
// [GPUResource]
|
|
void OnReleaseGPU() override;
|
|
};
|