mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
306 lines
10 KiB
Beef
306 lines
10 KiB
Beef
#if GE_GRAPHICS_DX11
|
|
|
|
using System;
|
|
using System.IO;
|
|
using DirectX.D3D11;
|
|
using DirectX.Common;
|
|
using DirectXTK;
|
|
using GlitchyEngine.Math;
|
|
using GlitchyEngine.Platform.DX11;
|
|
using System.Collections;
|
|
|
|
using internal GlitchyEngine.Renderer;
|
|
using internal GlitchyEngine.Platform.DX11;
|
|
|
|
namespace GlitchyEngine.Renderer
|
|
{
|
|
internal typealias NativeTex2DDesc = DirectX.D3D11.Texture2DDescription;
|
|
|
|
extension Texture
|
|
{
|
|
protected internal ID3D11ShaderResourceView* _nativeResourceView ~ _?.Release();
|
|
|
|
/** \brief Loads the texture from the specified path.
|
|
* @param path The path of the texture to load.
|
|
* @param texture The reference to the pointer that will hold the texture.
|
|
* @returns true if the texture was loaded successfully; false otherwise.
|
|
*/
|
|
protected bool LoadDdsResourcePlatform<T>(Stream stream, ref T* texture) where T : ID3D11Resource
|
|
{
|
|
Debug.Profiler.ProfileResourceFunction!();
|
|
|
|
uint8[] ddsData = new:ScopedAlloc! uint8[stream.Length];
|
|
|
|
var result = stream.TryRead(ddsData);
|
|
|
|
if (result case .Err(let err))
|
|
{
|
|
Log.EngineLogger.Error($"Failed to read texture data from stream. Error: {err}");
|
|
}
|
|
|
|
((ID3D11Resource*)texture)?.Release();
|
|
_nativeResourceView?.Release();
|
|
|
|
HResult loadResult = DDSTextureLoader.CreateDDSTextureFromMemory(NativeDevice,
|
|
ddsData.Ptr, (uint)ddsData.Count, (.)&texture, &_nativeResourceView);
|
|
|
|
if(loadResult.Failed)
|
|
{
|
|
Log.EngineLogger.Error($"Failed to load texture. Error({(int)loadResult}): {loadResult}");
|
|
|
|
ReleaseAndNullify!(texture);
|
|
ReleaseAndNullify!(_nativeResourceView);
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
extension Texture2DDesc
|
|
{
|
|
public NativeTex2DDesc ToNative()
|
|
{
|
|
NativeTex2DDesc desc;
|
|
|
|
desc.Width = Width;
|
|
desc.Height = Height;
|
|
desc.MipLevels = MipLevels;
|
|
desc.ArraySize = ArraySize;
|
|
desc.Format = Format;
|
|
|
|
// TODO: missing options
|
|
desc.SampleDesc = .(1, 0);
|
|
desc.Usage = (.)Usage;
|
|
desc.BindFlags = .ShaderResource;
|
|
|
|
desc.CpuAccessFlags = (.)CpuAccess;
|
|
desc.MiscFlags = .None;
|
|
|
|
return desc;
|
|
}
|
|
|
|
public static explicit operator NativeTex2DDesc(Self desc) => desc.ToNative();
|
|
}
|
|
|
|
extension Texture2D
|
|
{
|
|
protected internal ID3D11Texture2D* nativeTexture ~ _?.Release();
|
|
protected internal NativeTex2DDesc nativeDesc;
|
|
|
|
public override uint32 Width => nativeDesc.Width;
|
|
public override uint32 Height => nativeDesc.Height;
|
|
public override uint32 ArraySize => nativeDesc.ArraySize;
|
|
public override uint32 MipLevels => nativeDesc.MipLevels;
|
|
|
|
protected override void LoadDdsPlatform(Stream stream)
|
|
{
|
|
Debug.Profiler.ProfileResourceFunction!();
|
|
|
|
LoadDdsResourcePlatform(stream, ref nativeTexture);
|
|
|
|
let resType = nativeTexture.GetResourceType();
|
|
Log.EngineLogger.Assert(resType == .Texture2D, scope $"The texture \"{_path}\" is not a 2D texture (it is {resType}).");
|
|
|
|
nativeTexture.GetDescription(out nativeDesc);
|
|
}
|
|
|
|
protected override void CreateTexturePlatform(Texture2DDesc desc, bool isRenderTarget, void* data, uint32 linePitch)
|
|
{
|
|
Debug.Profiler.ProfileResourceFunction!();
|
|
|
|
PrepareTexturePlatform(desc, isRenderTarget);
|
|
InternalCreateTexture(data, linePitch, 0);
|
|
}
|
|
|
|
private void InternalCreateTexture(void* data, uint32 linePitch, uint32 slicePitch)
|
|
{
|
|
Debug.Profiler.ProfileResourceFunction!();
|
|
|
|
SubresourceData resData = .(data, linePitch, slicePitch);
|
|
|
|
// If data is null, set subresourceData null
|
|
SubresourceData* resDataPtr = data == null ? null : &resData;
|
|
|
|
var result = NativeDevice.CreateTexture2D(ref nativeDesc, resDataPtr, &nativeTexture);
|
|
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create texture 2D. Error ({result.Underlying}): {result}");
|
|
|
|
result = NativeDevice.CreateShaderResourceView(nativeTexture, null, &_nativeResourceView);
|
|
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create texture view. Error ({result.Underlying}): {result}");
|
|
}
|
|
|
|
protected override void PrepareTexturePlatform(Texture2DDesc desc, bool isRenderTarget)
|
|
{
|
|
Debug.Profiler.ProfileResourceFunction!();
|
|
|
|
nativeTexture?.Release();
|
|
nativeTexture = null;
|
|
_nativeResourceView?.Release();
|
|
_nativeResourceView = null;
|
|
|
|
nativeDesc = (NativeTex2DDesc)desc;
|
|
|
|
if(isRenderTarget)
|
|
nativeDesc.BindFlags |= .RenderTarget;
|
|
}
|
|
|
|
// TODO: Update Texture Arrays!
|
|
protected override Result<void> PlatformSetData(void* data, uint32 elementSize, uint32 destX,
|
|
uint32 destY, uint32 destWidth, uint32 destHeight, uint32 arraySlice, uint32 mipLevel, GlitchyEngine.Renderer.MapType mapType)
|
|
{
|
|
Debug.Profiler.ProfileResourceFunction!();
|
|
|
|
if(nativeTexture == null)
|
|
{
|
|
if(destX == 0 && destY == 0 && destWidth == nativeDesc.Width && destHeight == nativeDesc.Height)
|
|
{
|
|
// We can pass the data while creating the buffer, so we can return here.
|
|
var rowPitch = elementSize * destWidth;
|
|
return InternalCreateTexture(data, rowPitch, rowPitch * destHeight);
|
|
}
|
|
else
|
|
{
|
|
// If we don't set the entire texture we need to create the texture with unknown content and set it manually
|
|
Log.EngineLogger.Assert(nativeDesc.Usage != .Immutable, "The entire immutable texture has to be initialized.");
|
|
InternalCreateTexture(null, 0, 0);
|
|
}
|
|
}
|
|
|
|
//Log.EngineLogger.AssertDebug();
|
|
// TODO: Debug.Assert(dstByteOffset + byteLength <= _description.Size, "The destination offset and byte length are too long for the target buffer.");
|
|
|
|
uint32 subresourceIndex = D3D11.CalcSubresource(mipLevel, arraySlice, nativeDesc.MipLevels);
|
|
|
|
switch(nativeDesc.Usage)
|
|
{
|
|
case .Default:
|
|
Box dataBox = .(destX, destY, 0, destX + destWidth, destY + destHeight, 1);
|
|
var rowPitch = elementSize * destWidth;
|
|
NativeContext.UpdateSubresource(nativeTexture, subresourceIndex, &dataBox, data, rowPitch, rowPitch * destHeight);
|
|
case .Dynamic:
|
|
Runtime.NotImplemented();
|
|
/*
|
|
Log.EngineLogger.Assert(mapType.CanWrite, "The map type has to have write access.");
|
|
// Todo: DoNotWaitFlag
|
|
MappedSubresource map = ?;
|
|
_context.nativeContext.Map(nativeBuffer, 0, (.)mapType, .None, &map);
|
|
Internal.MemCpy(((uint8*)map.Data) + dstByteOffset, data, byteLength);
|
|
_context.nativeContext.Unmap(nativeBuffer, 0);
|
|
*/
|
|
case .Immutable:
|
|
Log.EngineLogger.Error("Can't set the data of an immutable resource.");
|
|
return .Err;
|
|
default:
|
|
Log.EngineLogger.Error($"Unknown resource usage: {nativeDesc.Usage}");
|
|
return .Err;
|
|
}
|
|
|
|
return .Ok;
|
|
}
|
|
|
|
public static override void CopySubresourceRegion(Texture2D source, Texture2D destination,
|
|
ResourceBox sourceBox = default, uint32 destX = 0, uint32 destY = 0,
|
|
uint32 srcArraySlice = 0, uint32 srcMipSlice = 0, uint32 destArraySlice = 0, uint32 destMipSlice = 0)
|
|
{
|
|
Debug.Profiler.ProfileResourceFunction!();
|
|
|
|
Log.EngineLogger.AssertDebug(source != destination || srcArraySlice != destArraySlice
|
|
|| srcMipSlice != destMipSlice, "Cannot copy from and to the same sub resource.");
|
|
|
|
Log.EngineLogger.AssertDebug(source.nativeDesc.Format == destination.nativeDesc.Format,
|
|
"Source and destination resources must have the same texel-format.");
|
|
|
|
var sourceBox;
|
|
|
|
if(sourceBox.Right == 0)
|
|
{
|
|
sourceBox.Right = source.Width;
|
|
sourceBox.Bottom = source.Height;
|
|
sourceBox.Back = 1;
|
|
}
|
|
|
|
// Make sure the destination was initialized
|
|
if(destination.nativeTexture == null)
|
|
destination.InternalCreateTexture(null, 0, 0);
|
|
|
|
NativeContext.CopySubresourceRegion(destination.nativeTexture,
|
|
D3D11.CalcSubresource(destMipSlice, destArraySlice, destination.MipLevels), destX, destY, 0,
|
|
source.nativeTexture, D3D11.CalcSubresource(srcMipSlice, srcArraySlice, source.MipLevels), (.)&sourceBox);
|
|
}
|
|
|
|
public override void CopyTo(Texture2D destination)
|
|
{
|
|
Debug.Profiler.ProfileResourceFunction!();
|
|
|
|
if(nativeTexture == null)
|
|
return;
|
|
|
|
Log.EngineLogger.AssertDebug(nativeDesc.Format == destination.nativeDesc.Format,
|
|
"Source and destination resources must have the same texel-format.");
|
|
|
|
// Make sure the destination was initialized
|
|
if(destination.nativeTexture == null)
|
|
destination.InternalCreateTexture(null, 0, 0);
|
|
|
|
uint32 arraySlices = Math.Min(ArraySize, destination.ArraySize);
|
|
uint32 mipSlices = Math.Min(MipLevels, destination.MipLevels);
|
|
|
|
for(uint32 arraySlice < arraySlices)
|
|
for(uint32 mipSlice < mipSlices)
|
|
{
|
|
Box sourceBox = .(0, 0, 0, Width, Height, 1);
|
|
|
|
NativeContext.CopySubresourceRegion(destination.nativeTexture,
|
|
D3D11.CalcSubresource(mipSlice, arraySlice, destination.MipLevels), 0, 0, 0,
|
|
nativeTexture, D3D11.CalcSubresource(mipSlice, arraySlice, MipLevels), (.)&sourceBox);
|
|
}
|
|
}
|
|
|
|
protected override TextureViewBinding PlatformGetViewBinding()
|
|
{
|
|
return .(_nativeResourceView, _samplerState?.nativeSamplerState);
|
|
}
|
|
|
|
protected override void PlatformSneakySwappyTexture(Texture2D otherTexture)
|
|
{
|
|
Swap!(nativeDesc, otherTexture.nativeDesc);
|
|
Swap!(nativeTexture, otherTexture.nativeTexture);
|
|
Swap!(_nativeResourceView, otherTexture._nativeResourceView);
|
|
}
|
|
}
|
|
|
|
extension TextureCube
|
|
{
|
|
protected internal ID3D11Texture2D* nativeTexture ~ _?.Release();
|
|
protected internal NativeTex2DDesc nativeDesc;
|
|
|
|
public override uint32 Width => nativeDesc.Width;
|
|
public override uint32 Height => nativeDesc.Height;
|
|
public override uint32 ArraySize => nativeDesc.ArraySize / 6;
|
|
public override uint32 MipLevels => nativeDesc.MipLevels;
|
|
|
|
protected override void LoadTexturePlatform(Stream stream)
|
|
{
|
|
Debug.Profiler.ProfileResourceFunction!();
|
|
|
|
LoadDdsResourcePlatform(stream, ref nativeTexture);
|
|
|
|
let resType = nativeTexture.GetResourceType();
|
|
Log.EngineLogger.Assert(resType == .Texture2D, scope $"The texture \"{_path}\" is not a texture cube (it is {resType}).");
|
|
|
|
nativeTexture.GetDescription(out nativeDesc);
|
|
|
|
Log.EngineLogger.Assert(nativeDesc.MiscFlags.HasFlag(.TextureCube), scope $"The texture \"{_path}\" is not a texture cube.");
|
|
// TODO: load fallback texture
|
|
}
|
|
|
|
protected override TextureViewBinding PlatformGetViewBinding()
|
|
{
|
|
return .(_nativeResourceView, _samplerState.nativeSamplerState);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|