#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(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 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