You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/CryEngine/RenderDll/XRenderD3D9/DXMETAL/Implementation/GLResource.cpp

3194 lines
126 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Implements the resource related functions
#include "RenderDll_precompiled.h"
#include "GLResource.hpp"
#include "METALContext.hpp"
#include "MetalDevice.hpp"
#include <DriverD3D.h>
namespace NCryMetal
{
enum
{
MIN_MAPPED_RESOURCE_ALIGNMENT = 64, // DX10+ mapped resources are 16-aligned but GL_ARB_map_buffer_alignment ensures 64-alignment for AVX
};
MemRingBufferStorage GetMemAllocModeBasedOnSize(const size_t size)
{
MemRingBufferStorage memAllocMode = MEM_SHARED_RINGBUFFER;
#if defined(AZ_PLATFORM_MAC)
if (size > FASTBUFFER_SIZE_THRESHHOLD)
{
memAllocMode = MEM_MANAGED_RINGBUFFER;
}
#endif
return memAllocMode;
}
id<MTLBuffer> GetMtlBufferBasedOnSize(const SBuffer* pBuffer)
{
if (!pBuffer)
{
return NULL;
}
id<MTLBuffer> buffer = pBuffer->m_BufferShared;
#if defined(AZ_PLATFORM_MAC)
if (pBuffer->m_BufferManaged && GetMemAllocModeBasedOnSize(pBuffer->m_uMapSize) == MEM_MANAGED_RINGBUFFER)
{
buffer = pBuffer->m_BufferManaged;
}
#endif
return buffer;
}
uint32 GetRowPitch(uint32 uWidth, uint32 uRowBytes, const SGIFormatInfo* pFormatInfo)
{
uint32 uNumElementsPerRow(uRowBytes * pFormatInfo->m_pTexture->m_uBlockWidth / pFormatInfo->m_pTexture->m_uNumBlockBytes);
return uNumElementsPerRow == uWidth ? 0 : uNumElementsPerRow;
}
uint32 GetImagePitch(uint32 uHeight, uint32 uImageBytes, uint32 uRowBytes)
{
uint32 uNumRowsPerImage(uImageBytes / uRowBytes);
return uNumRowsPerImage == uHeight ? 0 : uNumRowsPerImage;
}
struct STexBox
{
STexPos m_kOffset;
STexSize m_kSize;
};
struct SPackedLayout
{
uint32 m_uRowPitch;
uint32 m_uImagePitch;
uint32 m_uTextureSize;
};
static GLint GetMaxMipLevels(const D3D11_TEXTURE1D_DESC& kTexDesc)
{
return (GLint)IntegerLog2(kTexDesc.Width);
}
static GLint GetMaxMipLevels(const D3D11_TEXTURE2D_DESC& kTexDesc)
{
return (GLint)max(IntegerLog2(kTexDesc.Width), IntegerLog2(kTexDesc.Height));
}
static GLint GetMaxMipLevels(const D3D11_TEXTURE3D_DESC& kTexDesc)
{
return (GLint)max(max(IntegerLog2(kTexDesc.Width), IntegerLog2(kTexDesc.Height)), IntegerLog2(kTexDesc.Depth));
}
template <typename TextureDesc>
static GLint GetNumMipLevels(const TextureDesc& kTexDesc)
{
return kTexDesc.MipLevels != 0 ? kTexDesc.MipLevels : GetMaxMipLevels(kTexDesc);
}
static STexSize GetMipSize(STexture* pTexture, GLint iLevel, const SGIFormatInfo* pFormat, bool bClampToBlockSize)
{
STexSize kMinSize(1, 1, 1);
if (bClampToBlockSize && pFormat->m_pTexture->m_bCompressed)
{
kMinSize = STexSize(
pFormat->m_pTexture->m_uBlockWidth,
pFormat->m_pTexture->m_uBlockHeight,
pFormat->m_pTexture->m_uBlockDepth);
}
return STexSize(
max(kMinSize.x, pTexture->m_iWidth >> iLevel),
max(kMinSize.y, pTexture->m_iHeight >> iLevel),
max(kMinSize.z, pTexture->m_iDepth >> iLevel));
}
void GetTextureBox(STexBox& kTexBox, STexture* pTexture, GLint iLevel, const SGIFormatInfo* pFormat, bool bClampToBlockSize)
{
kTexBox.m_kOffset = STexPos(0, 0, 0);
kTexBox.m_kSize = GetMipSize(pTexture, iLevel, pFormat, bClampToBlockSize);
}
void GetTextureBox(STexBox& kTexBox, STexture* pTexture, GLint iLevel, const D3D11_BOX* pBox, const SGIFormatInfo* pFormat, bool bClampToBlockSize)
{
if (pBox != NULL)
{
kTexBox.m_kOffset = STexPos(pBox->left, pBox->top, pBox->front);
kTexBox.m_kSize = STexSize(pBox->right - pBox->left, pBox->bottom - pBox->top, pBox->back - pBox->front);
}
else
{
GetTextureBox(kTexBox, pTexture, iLevel, pFormat, bClampToBlockSize);
}
}
struct STex1DBase
{
static GLsizei GetBCImageSize(STexSize kSize, const STextureFormat* pTexFormat)
{
return pTexFormat->m_uNumBlockBytes *
((kSize.x + pTexFormat->m_uBlockWidth - 1) / pTexFormat->m_uBlockWidth);
}
};
struct STex2DBase
{
static GLsizei GetBCImageSize(STexSize kSize, const STextureFormat* pTexFormat)
{
return pTexFormat->m_uNumBlockBytes *
((kSize.x + pTexFormat->m_uBlockWidth - 1) / pTexFormat->m_uBlockWidth) *
((kSize.y + pTexFormat->m_uBlockHeight - 1) / pTexFormat->m_uBlockHeight);
}
};
struct STex3DBase
{
static GLsizei GetBCImageSize(STexSize kSize, const STextureFormat* pTexFormat)
{
return pTexFormat->m_uNumBlockBytes *
((kSize.x + pTexFormat->m_uBlockWidth - 1) / pTexFormat->m_uBlockWidth) *
((kSize.y + pTexFormat->m_uBlockHeight - 1) / pTexFormat->m_uBlockHeight) *
((kSize.z + pTexFormat->m_uBlockDepth - 1) / pTexFormat->m_uBlockDepth);
}
};
struct SDefaultTex1DBase
: STex1DBase
{
static void TexStorage(STexture* pTexture, STexSize kSize, GLsizei iLevels, const SGIFormatInfo* pFormat, id<MTLDevice> mtlDevice, const uint32 uBindFlags)
{
assert(pFormat->m_pTexture);
DXMETAL_NOT_IMPLEMENTED
}
};
struct SDefaultTex2DBase
: STex2DBase
{
typedef SDefaultTex1DBase TArrayElement;
static void TexStorage(STexture* pTexture, STexSize kSize, GLsizei iLevels, const SGIFormatInfo* pFormat, id<MTLDevice> mtlDevice, const uint32 uBindFlags)
{
const STextureFormat* pTexFormat = pFormat->m_pTexture;
assert(pTexFormat);
// Confetti BEGIN: Igor Lobanchikov
bool bHasStecnilAttachment = false;
MTLPixelFormat eMetalFormat = pTexFormat->m_eMetalFormat;
bool isDepthStencilBuffer = pFormat->m_eTypelessFormat == eGIF_R32G8X24_TYPELESS || pFormat->m_eDXGIFormat == DXGI_FORMAT_R32G8X24_TYPELESS || pFormat->m_eDXGIFormat == DXGI_FORMAT_R16_TYPELESS || pFormat->m_eDXGIFormat == DXGI_FORMAT_R32_TYPELESS ;
// Igor: Special handling for the texture which is actually 2 textures
if (isDepthStencilBuffer)
{
bHasStecnilAttachment = true;
#if defined(AZ_PLATFORM_MAC)
//The OSX_GPUFamily1_v1 feature set does not support separate depth and stencil render targets.
//If these render targets are needed, use the following newly introduced depth/stencil pixel formats
//to set the same texture as both the depth and stencil render target
eMetalFormat = MTLPixelFormatDepth32Float_Stencil8;
#else
eMetalFormat = MTLPixelFormatDepth32Float;
#endif
}
if (eMetalFormat != MTLPixelFormatInvalid)
{
MTLTextureDescriptor* Desc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:eMetalFormat
width:kSize.x
height:kSize.y
mipmapped:(iLevels > 1)];
switch (pTexture->m_eTextureType)
{
case MTLTextureTypeCube:
assert(pTexture->m_uNumElements == 6);
Desc.textureType = MTLTextureTypeCube;
break;
case MTLTextureType2D:
assert(pTexture->m_uNumElements == 1);
break;
case MTLTextureType2DArray:
Desc.textureType = MTLTextureType2DArray;
Desc.arrayLength = pTexture->m_uNumElements;
break;
default:
DXGL_NOT_IMPLEMENTED;
}
Desc.mipmapLevelCount = iLevels;
if (uBindFlags & D3D11_BIND_RENDER_TARGET)
{
Desc.usage = MTLTextureUsageRenderTarget;
}
if (uBindFlags & D3D11_BIND_SHADER_RESOURCE)
{
Desc.usage |= MTLTextureUsageShaderRead;
}
if (isDepthStencilBuffer)
{
//MTLStorageModePrivate on OS X makes it so that this resource is stored
//in video memory for the GPU.
Desc.storageMode = MTLStorageModePrivate;
//Depth stencil buffer gets written into and sampled from.
Desc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
#if defined(AZ_PLATFORM_MAC)
//On osx the depth/stencil texture is merged. You need to create a different
//texture view to access stencil data. Hence we need this flag.
Desc.usage |= MTLTextureUsagePixelFormatView;
#endif
}
else if(CTexture::IsDeviceFormatTypeless(pFormat->m_eDXGIFormat))
{
//Apple recommendation -> For sRGB variant views, you don't need the PFV flag when: - running on
//iOS/tvOS 12.0 or newer - running on macOS 10.15 or newer
//However, on older OSs (and in macOS case, older GPUs) you are still required to set the flag.
#if defined(AZ_COMPILER_CLANG) && AZ_COMPILER_CLANG >= 9 //@available was added in Xcode 9
if (@available(macOS 10.15, iOS 12.0, *))
{
//No need to do anything but if we want to add non sRGB view related flags it would go here.
}
else
{
Desc.usage |= MTLTextureUsagePixelFormatView;
}
#endif
}
pTexture->m_Texture = [mtlDevice newTextureWithDescriptor:Desc];
if (!pTexture->m_Texture)
{
LOG_METAL_SHADER_ERRORS(@ "Failed to create texture: %@", Desc);
}
else if (isDepthStencilBuffer)
{
#if defined(AZ_PLATFORM_MAC)
pTexture->m_StencilTexture = pTexture->m_Texture;
#else
Desc.pixelFormat = MTLPixelFormatStencil8;
pTexture->m_StencilTexture = [mtlDevice newTextureWithDescriptor: Desc];
if (!pTexture->m_StencilTexture)
{
LOG_METAL_SHADER_ERRORS(@ "Failed to create stencil attachment: %@", Desc);
}
#endif
}
}
// Confetti End: Igor Lobanchikov
}
template <typename T>
static void SetLayerComponent(STexVec<T>& kVec, T kLayer)
{
kVec.y = kLayer;
}
};
struct SDefaultTex3DBase
: STex3DBase
{
typedef SDefaultTex2DBase TArrayElement;
static void TexStorage(STexture* pTexture, STexSize kSize, GLsizei iLevels, const SGIFormatInfo* pFormat, id<MTLDevice> mtlDevice, const uint32 uBindFlags)
{
const STextureFormat* pTexFormat = pFormat->m_pTexture;
assert(pTexFormat);
// Confetti BEGIN: Igor Lobanchikov
MTLPixelFormat eMetalFormat = pTexFormat->m_eMetalFormat;
assert(pTexture->m_eTextureType == MTLTextureType3D || pTexture->m_eTextureType == MTLTextureType2DArray);
assert(pTexture->m_uNumElements == 1 || pTexture->m_eTextureType != MTLTextureType3D);
if (eMetalFormat != MTLPixelFormatInvalid)
{
MTLTextureDescriptor* Desc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:eMetalFormat
width:kSize.x
height:kSize.y
mipmapped:(iLevels > 1)];
Desc.depth = kSize.z;
Desc.textureType = MTLTextureType3D;
Desc.mipmapLevelCount = iLevels;
pTexture->m_Texture = [mtlDevice newTextureWithDescriptor:Desc];
if (!pTexture->m_Texture)
{
LOG_METAL_SHADER_ERRORS(@ "Failed to create texture: %@", Desc);
}
}
// Confetti End: Igor Lobanchikov
}
template <typename T>
static void SetLayerComponent(STexVec<T>& kVec, T kLayer)
{
kVec.z = kLayer;
}
};
struct STexCompressed
{
static bool GetPackedRange(const STexBox& kPixels, STexBox* pPackedRange, const SGIFormatInfo* pFormat)
{
const STextureFormat* pTexFormat(pFormat->m_pTexture);
if ((kPixels.m_kOffset.x % pTexFormat->m_uBlockWidth) != 0 ||
(kPixels.m_kOffset.y % pTexFormat->m_uBlockHeight) != 0 ||
(kPixels.m_kOffset.z % pTexFormat->m_uBlockDepth) != 0 ||
(kPixels.m_kSize.x % pTexFormat->m_uBlockWidth) != 0 ||
(kPixels.m_kSize.y % pTexFormat->m_uBlockHeight) != 0 ||
(kPixels.m_kSize.z % pTexFormat->m_uBlockDepth) != 0)
{
return false;
}
pPackedRange->m_kOffset.x = pTexFormat->m_uNumBlockBytes * kPixels.m_kOffset.x / pTexFormat->m_uBlockWidth;
pPackedRange->m_kSize.x = pTexFormat->m_uNumBlockBytes * kPixels.m_kSize.x / pTexFormat->m_uBlockWidth;
pPackedRange->m_kOffset.y = kPixels.m_kOffset.y / pTexFormat->m_uBlockHeight;
pPackedRange->m_kSize.y = kPixels.m_kSize.y / pTexFormat->m_uBlockHeight;
pPackedRange->m_kOffset.z = kPixels.m_kOffset.z / pTexFormat->m_uBlockDepth;
pPackedRange->m_kSize.z = kPixels.m_kSize.z / pTexFormat->m_uBlockDepth;
return true;
}
};
struct STexUncompressed
{
static bool GetPackedRange(const STexBox& kPixels, STexBox* pPackedRange, const SGIFormatInfo* pFormat)
{
uint32 uPixelBytes(pFormat->m_pTexture->m_uNumBlockBytes);
pPackedRange->m_kOffset.x = kPixels.m_kOffset.x * uPixelBytes;
pPackedRange->m_kSize.x = kPixels.m_kSize.x * uPixelBytes;
pPackedRange->m_kOffset.y = kPixels.m_kOffset.y;
pPackedRange->m_kSize.y = kPixels.m_kSize.y;
pPackedRange->m_kOffset.z = kPixels.m_kOffset.z;
pPackedRange->m_kSize.z = kPixels.m_kSize.z;
return true;
}
};
struct STex1DUncompressed
: SDefaultTex1DBase
, STexUncompressed
{
// Confetti BEGIN: Igor Lobanchikov
static void TexSubImage(STexture* pTexture, uint32 slice, GLint iLevel, STexBox kBox, const STextureFormat* pTexFormat, const GLvoid* pData, uint32 uRowPitch, uint32 uImagePitch)
{
MTLRegion Region = MTLRegionMake3D(kBox.m_kOffset.x, 0, 0, kBox.m_kSize.x, 1, 1);
assert(kBox.m_kSize.y < 2);
[pTexture->m_Texture replaceRegion: Region mipmapLevel: iLevel slice: slice withBytes: pData bytesPerRow: uRowPitch bytesPerImage: uImagePitch];
}
// Confetti End: Igor Lobanchikov
static void GetPackedLayout(STexSize kRect, const SGIFormatInfo* pFormat, SPackedLayout* pLayout)
{
pLayout->m_uRowPitch = kRect.x * pFormat->m_pTexture->m_uNumBlockBytes;
pLayout->m_uImagePitch = pLayout->m_uRowPitch;
pLayout->m_uTextureSize = pLayout->m_uRowPitch;
}
};
struct STex2DUncompressed
: SDefaultTex2DBase
, STexUncompressed
{
// Confetti BEGIN: Igor Lobanchikov
static void TexSubImage(STexture* pTexture, uint32 slice, GLint iLevel, STexBox kBox, const STextureFormat* pTexFormat, const GLvoid* pData, uint32 uRowPitch, uint32 uImagePitch)
{
MTLRegion Region = MTLRegionMake3D(kBox.m_kOffset.x, kBox.m_kOffset.y, 0, kBox.m_kSize.x, kBox.m_kSize.y, 1);
assert(kBox.m_kSize.z < 2);
[pTexture->m_Texture replaceRegion: Region mipmapLevel: iLevel slice: slice withBytes: pData bytesPerRow: uRowPitch bytesPerImage: uImagePitch];
}
// Confetti End: Igor Lobanchikov
static void GetPackedLayout(STexSize kRect, const SGIFormatInfo* pFormat, SPackedLayout* pLayout)
{
pLayout->m_uRowPitch = kRect.x * pFormat->m_pTexture->m_uNumBlockBytes;
pLayout->m_uImagePitch = kRect.y * pLayout->m_uRowPitch;
pLayout->m_uTextureSize = pLayout->m_uImagePitch;
}
};
struct STex3DUncompressed
: SDefaultTex3DBase
, STexUncompressed
{
// Confetti BEGIN: Igor Lobanchikov
static void TexSubImage(STexture* pTexture, uint32 slice, GLint iLevel, STexBox kBox, const STextureFormat* pTexFormat, const GLvoid* pData, uint32 uRowPitch, uint32 uImagePitch)
{
MTLRegion Region = MTLRegionMake3D(kBox.m_kOffset.x, kBox.m_kOffset.y, kBox.m_kOffset.z, kBox.m_kSize.x, kBox.m_kSize.y, kBox.m_kSize.z);
assert(slice == 0);
[pTexture->m_Texture replaceRegion: Region mipmapLevel: iLevel slice: slice withBytes: pData bytesPerRow: uRowPitch bytesPerImage: uImagePitch];
}
// Confetti End: Igor Lobanchikov
static void GetPackedLayout(STexSize kRect, const SGIFormatInfo* pFormat, SPackedLayout* pLayout)
{
pLayout->m_uRowPitch = kRect.x * pFormat->m_pTexture->m_uNumBlockBytes;
pLayout->m_uImagePitch = kRect.y * pLayout->m_uRowPitch;
pLayout->m_uTextureSize = kRect.z * pLayout->m_uImagePitch;
}
};
struct STex1DCompressed
: SDefaultTex1DBase
, STexCompressed
{
// Confetti BEGIN: Igor Lobanchikov
static void TexSubImage(STexture* pTexture, uint32 slice, GLint iLevel, STexBox kBox, const STextureFormat* pTexFormat, const GLvoid* pData, uint32 uRowPitch, uint32 uImagePitch)
{
MTLRegion Region = MTLRegionMake3D(kBox.m_kOffset.x, 0, 0, kBox.m_kSize.x, 1, 1);
assert(kBox.m_kSize.y < 2);
//MTLPixelFormatPVRTC_RGB_2BPP/MTLPixelFormatPVRTC_RGBA_4BPP_sRGB not supported on OSX GPUs
#if !defined AZ_PLATFORM_MAC
// Igor: Metal requires these to be 0 for PVR formats
if (pTexFormat->m_eMetalFormat >= MTLPixelFormatPVRTC_RGB_2BPP && pTexFormat->m_eMetalFormat <= MTLPixelFormatPVRTC_RGBA_4BPP_sRGB)
{
uRowPitch = 0;
uImagePitch = 0;
}
#endif
[pTexture->m_Texture replaceRegion: Region mipmapLevel: iLevel slice: slice withBytes: pData bytesPerRow: uRowPitch bytesPerImage: uImagePitch];
}
// Confetti End: Igor Lobanchikov
static void GetPackedLayout(STexSize kRect, const SGIFormatInfo* pFormat, SPackedLayout* pLayout)
{
pLayout->m_uRowPitch = GetBCImageSize(kRect, pFormat->m_pTexture);
pLayout->m_uImagePitch = pLayout->m_uRowPitch;
pLayout->m_uTextureSize = pLayout->m_uRowPitch;
}
};
struct STex2DCompressed
: SDefaultTex2DBase
, STexCompressed
{
// Confetti BEGIN: Igor Lobanchikov
static void TexSubImage(STexture* pTexture, uint32 slice, GLint iLevel, STexBox kBox, const STextureFormat* pTexFormat, const GLvoid* pData, uint32 uRowPitch, uint32 uImagePitch)
{
MTLRegion Region = MTLRegionMake3D(kBox.m_kOffset.x, kBox.m_kOffset.y, 0, kBox.m_kSize.x, kBox.m_kSize.y, 1);
assert(kBox.m_kSize.z < 2);
//MTLPixelFormatPVRTC_RGB_2BPP/MTLPixelFormatPVRTC_RGBA_4BPP_sRGB not supported on OSX GPUs
#if !defined AZ_PLATFORM_MAC
// Igor: Metal requires these to be 0 for PVR formats
if (pTexFormat->m_eMetalFormat >= MTLPixelFormatPVRTC_RGB_2BPP && pTexFormat->m_eMetalFormat <= MTLPixelFormatPVRTC_RGBA_4BPP_sRGB)
{
uRowPitch = 0;
uImagePitch = 0;
}
#endif
[pTexture->m_Texture replaceRegion: Region mipmapLevel: iLevel slice: slice withBytes: pData bytesPerRow: uRowPitch bytesPerImage: uImagePitch];
}
// Confetti End: Igor Lobanchikov
static void GetPackedLayout(STexSize kRect, const SGIFormatInfo* pFormat, SPackedLayout* pLayout)
{
pLayout->m_uRowPitch = GetBCImageSize(STexSize(kRect.x, 1, 1), pFormat->m_pTexture);
pLayout->m_uImagePitch = GetBCImageSize(kRect, pFormat->m_pTexture);
pLayout->m_uTextureSize = pLayout->m_uImagePitch;
}
};
struct STex3DCompressed
: SDefaultTex3DBase
, STexCompressed
{
// Confetti BEGIN: Igor Lobanchikov
static void TexSubImage(STexture* pTexture, uint32 slice, GLint iLevel, STexBox kBox, const STextureFormat* pTexFormat, const GLvoid* pData, uint32 uRowPitch, uint32 uImagePitch)
{
MTLRegion Region = MTLRegionMake3D(kBox.m_kOffset.x, kBox.m_kOffset.y, kBox.m_kOffset.z, kBox.m_kSize.x, kBox.m_kSize.y, kBox.m_kSize.z);
assert(slice == 0);
//MTLPixelFormatPVRTC_RGB_2BPP/MTLPixelFormatPVRTC_RGBA_4BPP_sRGB not supported on OSX GPUs
#if !defined AZ_PLATFORM_MAC
// Igor: Metal requires these to be 0 for PVR formats
if (pTexFormat->m_eMetalFormat >= MTLPixelFormatPVRTC_RGB_2BPP && pTexFormat->m_eMetalFormat <= MTLPixelFormatPVRTC_RGBA_4BPP_sRGB)
{
uRowPitch = 0;
uImagePitch = 0;
}
#endif
[pTexture->m_Texture replaceRegion: Region mipmapLevel: iLevel slice: slice withBytes: pData bytesPerRow: uRowPitch bytesPerImage: uImagePitch];
}
// Confetti End: Igor Lobanchikov
static void GetPackedLayout(STexSize kRect, const SGIFormatInfo* pFormat, SPackedLayout* pLayout)
{
pLayout->m_uRowPitch = GetBCImageSize(STexSize(kRect.x, 1, 1), pFormat->m_pTexture);
pLayout->m_uImagePitch = GetBCImageSize(STexSize(kRect.x, kRect.y, 1), pFormat->m_pTexture);
pLayout->m_uTextureSize = GetBCImageSize(kRect, pFormat->m_pTexture);
}
};
template <typename Interface>
struct SSingleTexImpl
: Interface
{
static void InitializeStorage(STexture* pTexture, uint32, const SGIFormatInfo* pFormat, CDevice* pDevice, const uint32 uBindFlags)
{
Interface::TexStorage(pTexture, GetMipSize(pTexture, 0, pFormat, false), pTexture->m_uNumMipLevels, pFormat, pDevice->GetMetalDevice(), uBindFlags);
}
static void UploadImage(STexture* pTexture, STexSubresourceID kSubID, STexBox kBox, const void* pSrcData, uint32 uSrcRowPitch, uint32 uSrcDepthPitch, CContext* pContext, const SGIFormatInfo* pFormat)
{
assert((kSubID.m_uElement == 0) ||
(pTexture->m_eTextureType == MTLTextureTypeCube && kSubID.m_uElement < 6));
// Confetti BEGIN: Igor Lobanchikov
Interface::TexSubImage(pTexture, kSubID.m_uElement, kSubID.m_iMipLevel, kBox, pFormat->m_pTexture, pSrcData, uSrcRowPitch, uSrcDepthPitch);
// Confetti End: Igor Lobanchikov
}
static void DownloadImage(STexture* pTexture, STexSubresourceID kSubID, STexBox kBox, void* pDstData, uint32 uDstRowPitch, uint32 uDstDepthPitch, CContext* pContext, const SGIFormatInfo* pFormat)
{
DXMETAL_NOT_IMPLEMENTED
}
static void Map(STexture* pTexture, STexSubresourceID kSubID, bool bDownload, SMappedSubTexture& kMappedSubTex, CContext* pContext, const SGIFormatInfo* pFormat)
{
STexBox kBox;
GetTextureBox(kBox, pTexture, kSubID.m_iMipLevel, pFormat, true);
SPackedLayout kPackedLayout;
Interface::GetPackedLayout(kBox.m_kSize, pFormat, &kPackedLayout);
DXGL_TODO("Check if it's worth to keep an allocation pool");
kMappedSubTex.m_pBuffer = static_cast<uint8*>(Memalign(kPackedLayout.m_uTextureSize, MIN_MAPPED_RESOURCE_ALIGNMENT));
kMappedSubTex.m_uRowPitch = kPackedLayout.m_uRowPitch;
kMappedSubTex.m_uImagePitch = kPackedLayout.m_uImagePitch;
kMappedSubTex.m_uDataOffset = 0;
if (bDownload)
{
DownloadImage(pTexture, kSubID, kBox, kMappedSubTex.m_pBuffer, kPackedLayout.m_uRowPitch, kPackedLayout.m_uImagePitch, pContext, pFormat);
}
}
static void Unmap(STexture* pTexture, STexSubresourceID kSubID, const SMappedSubTexture& kMappedSubTex, CContext* pContext, const SGIFormatInfo* pFormat)
{
if (kMappedSubTex.m_bUpload)
{
STexBox kBox;
GetTextureBox(kBox, pTexture, kSubID.m_iMipLevel, pFormat, true);
UploadImage(pTexture, kSubID, kBox, kMappedSubTex.m_pBuffer, kMappedSubTex.m_uRowPitch, kMappedSubTex.m_uImagePitch, pContext, pFormat);
}
MemalignFree(kMappedSubTex.m_pBuffer);
}
};
template <typename Interface>
struct SArrayTexImpl
: Interface
{
static void InitializeStorage(STexture* pTexture, uint32, const SGIFormatInfo* pFormat, CDevice* pDevice, const uint32 uBindFlags)
{
STexSize kTexSize(GetMipSize(pTexture, 0, pFormat, false));
Interface::SetLayerComponent(kTexSize, (GLsizei)pTexture->m_uNumElements);
Interface::TexStorage(pTexture, kTexSize, pTexture->m_uNumMipLevels, pFormat, pDevice->GetMetalDevice(), uBindFlags);
}
static void UploadImage(STexture* pTexture, STexSubresourceID kSubID, STexBox kBox, const void* pSrcData, uint32 uSrcRowPitch, uint32 uSrcDepthPitch, CContext* pContext, const SGIFormatInfo* pFormat)
{
assert(pTexture->m_eTextureType != MTLTextureTypeCube);
Interface::SetLayerComponent(kBox.m_kOffset, (int)kSubID.m_uElement);
Interface::SetLayerComponent(kBox.m_kSize, 1);
// Confetti BEGIN: Igor Lobanchikov
Interface::TexSubImage(pTexture, kSubID.m_uElement, kSubID.m_iMipLevel, kBox, pFormat->m_pTexture, pSrcData, uSrcRowPitch, uSrcDepthPitch);
// Confetti End: Igor Lobanchikov
}
static void DownloadImage(STexture* pTexture, STexSubresourceID kSubID, STexBox kBox, void* pDstData, uint32 uDstRowPitch, uint32 uDstDepthPitch, CContext* pContext, const SGIFormatInfo* pFormat)
{
DXGL_NOT_IMPLEMENTED
}
static void Map(STexture* pTexture, STexSubresourceID kSubID, bool bDownload, SMappedSubTexture& kMappedSubTex, CContext* pContext, const SGIFormatInfo* pFormat)
{
DXMETAL_NOT_IMPLEMENTED
}
static void Unmap(STexture* pTexture, STexSubresourceID kSubID, const SMappedSubTexture& kMappedSubTex, CContext* pContext, const SGIFormatInfo* pFormat)
{
DXMETAL_NOT_IMPLEMENTED
}
};
template <typename Interface>
uint32 GetSystemMemoryTextureOffset(STexture* pTexture, const SGIFormatInfo* pFormat, STexSubresourceID kID)
{
uint32 uOffset(0);
uint32 uTotSize(0);
uint32 uLevel;
for (uLevel = 0; uLevel < pTexture->m_uNumMipLevels; ++uLevel)
{
STexSize kLevelSize(GetMipSize(pTexture, (GLint)uLevel, pFormat, true));
SPackedLayout kPackedLayout;
Interface::GetPackedLayout(kLevelSize, pFormat, &kPackedLayout);
uTotSize += kPackedLayout.m_uTextureSize;
// Keep every subresource aligned so that it can be directly mapped
uTotSize += MIN_MAPPED_RESOURCE_ALIGNMENT - 1;
uTotSize -= (uTotSize % MIN_MAPPED_RESOURCE_ALIGNMENT);
if (uLevel < kID.m_iMipLevel)
{
uOffset = uTotSize;
}
}
return uTotSize * kID.m_uElement + uOffset;
}
template <typename Interface>
struct SStagingTexImpl
: Interface
{
static void InitializeStorage(STexture* pTexture, uint32 uCPUAccess, const SGIFormatInfo* pFormat, CDevice* pDevice, const uint32 uBindFlags)
{
Interface::TexStorage(pTexture, GetMipSize(pTexture, 0, pFormat, false), pTexture->m_uNumMipLevels, pFormat, pDevice->GetMetalDevice(), uBindFlags);
{
STexSubresourceID kEndID = {aznumeric_cast<int32>(pTexture->m_uNumMipLevels),
static_cast<uint32>(pTexture->m_uNumElements)};
uint32 uMappedSize(GetSystemMemoryTextureOffset<Interface>(pTexture, pFormat, kEndID));
pTexture->m_pMapMemoryCopy = static_cast<uint8*>(Memalign(uMappedSize, MIN_MAPPED_RESOURCE_ALIGNMENT));
}
}
static void UploadImage(STexture* pTexture, STexSubresourceID kSubID, STexBox kBox, const void* pSrcData, uint32 uSrcRowPitch, uint32 uSrcDepthPitch, CContext* pContext, const SGIFormatInfo* pFormat)
{
DXMETAL_NOT_IMPLEMENTED
}
static void DownloadImage(STexture* pTexture, STexSubresourceID kSubID, STexBox kBox, const void* pDstData, uint32 uSrcRowPitch, uint32 uSrcDepthPitch, CContext* pContext, const SGIFormatInfo* pFormat)
{
DXGL_NOT_IMPLEMENTED
}
static void Map(STexture* pTexture, STexSubresourceID kSubID, bool bDownload, SMappedSubTexture& kMappedSubTex, CContext* pContext, const SGIFormatInfo* pFormat)
{
STexSize kSubSize(GetMipSize(pTexture, kSubID.m_iMipLevel, pFormat, true));
SPackedLayout kPackedLayout;
Interface::GetPackedLayout(kSubSize, pFormat, &kPackedLayout);
kMappedSubTex.m_pBuffer = pTexture->m_pMapMemoryCopy + GetSystemMemoryTextureOffset<Interface>(pTexture, pFormat, kSubID);
if (bDownload)
{
static ICVar* isScreenshot = gEnv->pConsole->GetCVar("e_ScreenShot");
static ICVar* isCaptureFrame = gEnv->pConsole->GetCVar("capture_frames");
if (isScreenshot->GetIVal() || isCaptureFrame->GetIVal())
{
//This will stall the GPU so be very careful when using it. Only use it when you absolutely need the
//work encoded by the current comman buffer.
pContext->FlushBlitEncoderAndWait();
}
MTLRegion region = {
{0, 0, 0}, {pTexture->m_Texture.width, pTexture->m_Texture.height, pTexture->m_Texture.depth}
};
[pTexture->m_Texture getBytes: kMappedSubTex.m_pBuffer
bytesPerRow: kPackedLayout.m_uRowPitch
bytesPerImage: kPackedLayout.m_uImagePitch
fromRegion: region
mipmapLevel: kSubID.m_iMipLevel
slice: kSubID.m_uElement];
}
kMappedSubTex.m_uRowPitch = kPackedLayout.m_uRowPitch;
kMappedSubTex.m_uImagePitch = kPackedLayout.m_uImagePitch;
kMappedSubTex.m_uDataOffset = 0;
}
static void Unmap(STexture* pTexture, STexSubresourceID kSubID, const SMappedSubTexture& kMappedSubTex, CContext*, const SGIFormatInfo*)
{
if (kMappedSubTex.m_bUpload)
{
MTLRegion region = {
{0, 0, 0}, {pTexture->m_Texture.width, pTexture->m_Texture.height, pTexture->m_Texture.depth}
};
[pTexture->m_Texture replaceRegion: region
mipmapLevel: kSubID.m_iMipLevel
slice: kSubID.m_uElement
withBytes: kMappedSubTex.m_pBuffer
bytesPerRow: kMappedSubTex.m_uRowPitch
bytesPerImage: kMappedSubTex.m_uImagePitch];
}
}
};
inline STexSubresourceID GetTexSubresourceID(STexture* pTexture, uint32 uSubresource)
{
STexSubresourceID kID;
kID.m_iMipLevel = uSubresource % pTexture->m_uNumMipLevels;
kID.m_uElement = uSubresource / pTexture->m_uNumMipLevels;
assert(kID.m_uElement < pTexture->m_uNumElements);
return kID;
}
template <typename Impl>
void UpdateTexSubresource(SResource* pResource, uint32 uSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, uint32 uSrcRowPitch, uint32 uSrcDepthPitch, CContext* pContext)
{
DXGL_SCOPED_PROFILE("UpdateTexSubresource")
STexture * pTexture(static_cast<STexture*>(pResource));
const SGIFormatInfo* pFormatInfo(GetGIFormatInfo(pTexture->m_eFormat));
assert(pFormatInfo != NULL);
assert(pFormatInfo->m_pTexture != NULL);
STexSubresourceID kSubID(GetTexSubresourceID(pTexture, uSubresource));
STexBox kTexBox;
GetTextureBox(kTexBox, pTexture, kSubID.m_iMipLevel, pDstBox, pFormatInfo, false);
Impl::UploadImage(pTexture, kSubID, kTexBox, pSrcData, uSrcRowPitch, uSrcDepthPitch, pContext, pFormatInfo);
}
template <typename Impl>
bool MapTexSubresource(SResource* pResource, uint32 uSubresource, D3D11_MAP eMapType, uint32 uMapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource, CContext* pContext)
{
DXGL_SCOPED_PROFILE("MapTexSubresource")
STexture * pTexture(static_cast<STexture*>(pResource));
const SGIFormatInfo* pFormatInfo(GetGIFormatInfo(pTexture->m_eFormat));
assert(pFormatInfo != NULL);
assert(pFormatInfo->m_pTexture != NULL);
if (uSubresource >= pTexture->m_kMappedSubTextures.size())
{
pTexture->m_kMappedSubTextures.resize(uSubresource + 1);
}
SMappedSubTexture& kMappedSubTexture(pTexture->m_kMappedSubTextures.at(uSubresource));
if (kMappedSubTexture.m_pBuffer != NULL)
{
DXGL_ERROR("Texture subresource is already mapped");
return false;
}
bool bDownload = (eMapType == D3D11_MAP_READ || eMapType == D3D11_MAP_READ_WRITE);
Impl::Map(pTexture, GetTexSubresourceID(pTexture, uSubresource), bDownload, kMappedSubTexture, pContext, pFormatInfo);
kMappedSubTexture.m_bUpload = (eMapType != D3D11_MAP_READ);
pMappedResource->pData = kMappedSubTexture.m_pBuffer + kMappedSubTexture.m_uDataOffset;
pMappedResource->RowPitch = kMappedSubTexture.m_uRowPitch;
pMappedResource->DepthPitch = kMappedSubTexture.m_uImagePitch;
return kMappedSubTexture.m_pBuffer != NULL;
}
template <typename Impl>
void UnmapTexSubresource(SResource* pResource, UINT uSubresource, CContext* pContext)
{
DXGL_SCOPED_PROFILE("UnmapTexSubresource")
STexture * pTexture(static_cast<STexture*>(pResource));
const SGIFormatInfo* pFormatInfo(GetGIFormatInfo(pTexture->m_eFormat));
assert(pFormatInfo != NULL);
assert(pFormatInfo->m_pTexture != NULL);
if (uSubresource >= pTexture->m_kMappedSubTextures.size())
{
pTexture->m_kMappedSubTextures.resize(uSubresource + 1);
}
SMappedSubTexture& kMappedSubTexture(pTexture->m_kMappedSubTextures.at(uSubresource));
if (kMappedSubTexture.m_pBuffer == NULL)
{
DXGL_ERROR("Texture subresource is not mapped");
return;
}
Impl::Unmap(pTexture, GetTexSubresourceID(pTexture, uSubresource), kMappedSubTexture, pContext, pFormatInfo);
kMappedSubTexture.m_pBuffer = NULL;
}
template <typename Impl>
void UnpackTexData(STexture* pTexture, STexSubresourceID kSubID, STexPos kOffset, STexSize kSize, const SMappedSubTexture& kDataLocation, CContext* pContext)
{
DXGL_SCOPED_PROFILE("UnpackTexData")
STexBox kBox;
kBox.m_kOffset = kOffset;
kBox.m_kSize = kSize;
const SGIFormatInfo* pFormat(GetGIFormatInfo(pTexture->m_eFormat));
Impl::UploadImage(pTexture, kSubID, kBox, kDataLocation.m_pBuffer + kDataLocation.m_uDataOffset, kDataLocation.m_uRowPitch, kDataLocation.m_uImagePitch, pContext, pFormat);
}
template <typename Impl>
void PackTexData(STexture* pTexture, STexSubresourceID kSubID, STexPos kOffset, STexSize kSize, const SMappedSubTexture& kDataLocation, CContext* pContext)
{
DXGL_SCOPED_PROFILE("PackTexData")
STexBox kBox;
kBox.m_kOffset = kOffset;
kBox.m_kSize = kSize;
const SGIFormatInfo* pFormat(GetGIFormatInfo(pTexture->m_eFormat));
Impl::DownloadImage(pTexture, kSubID, kBox, kDataLocation.m_pBuffer + kDataLocation.m_uDataOffset, kDataLocation.m_uRowPitch, kDataLocation.m_uImagePitch, pContext, pFormat);
}
template <typename Impl>
void InitializeTexture(STexture* pTexture, const D3D11_SUBRESOURCE_DATA* pInitialData, uint32 uCPUAccess, CDevice* pDevice /*CContext* pContext*/, const SGIFormatInfo* pFormatInfo, const uint32 uBindFlags)
{
pTexture->m_pfUpdateSubresource = &UpdateTexSubresource<Impl>;
pTexture->m_pfMapSubresource = &MapTexSubresource<Impl>;
pTexture->m_pfUnmapSubresource = &UnmapTexSubresource<Impl>;
Impl::InitializeStorage(pTexture, uCPUAccess, pFormatInfo, pDevice, uBindFlags);
if (pInitialData)
{
STexBox kMipBox;
kMipBox.m_kOffset = STexPos(0, 0, 0);
STexSubresourceID kSubID;
for (kSubID.m_uElement = 0; kSubID.m_uElement < pTexture->m_uNumElements; ++kSubID.m_uElement)
{
for (kSubID.m_iMipLevel = 0; kSubID.m_iMipLevel < (GLint)pTexture->m_uNumMipLevels; ++kSubID.m_iMipLevel)
{
kMipBox.m_kSize = GetMipSize(pTexture, kSubID.m_iMipLevel, pFormatInfo, false);
Impl::UploadImage(pTexture, kSubID, kMipBox, pInitialData->pSysMem, pInitialData->SysMemPitch, pInitialData->SysMemSlicePitch, /*pContext*/ 0, pFormatInfo);
++pInitialData;
}
}
}
}
SResource::SResource()
: m_pfUpdateSubresource(NULL)
, m_pfMapSubresource(NULL)
, m_pfUnmapSubresource(NULL)
{
}
SResource::SResource(const SResource& kOther)
: m_pfUpdateSubresource(kOther.m_pfUpdateSubresource)
, m_pfMapSubresource(kOther.m_pfMapSubresource)
, m_pfUnmapSubresource(kOther.m_pfUnmapSubresource)
{
}
SResource::~SResource()
{
}
STexture::STexture(GLsizei iWidth, GLsizei iHeight, GLsizei iDepth, MTLTextureType eTextureType, EGIFormat eFormat, uint32 uNumMipLevels, uint32 uNumElements)
: m_eTextureType(eTextureType)
, m_eFormat(eFormat)
, m_uNumMipLevels(uNumMipLevels)
, m_uNumElements(uNumElements)
, m_iWidth(iWidth)
, m_iHeight(iHeight)
, m_iDepth(iDepth)
, m_pShaderViewsHead(NULL)
, m_pOutputMergerViewsHead(NULL)
, m_pBoundModifier(NULL)
// Confetti BEGIN: Igor Lobanchikov
, m_Texture(nil)
, m_StencilTexture(nil)
, m_bClearDepth(false)
, m_bClearStencil(false)
, m_bBackBuffer(false)
, m_pMapMemoryCopy(NULL)
// Confetti End: Igor Lobanchikov
{
#if DXGL_FULL_EMULATION
m_uNumElements = max(1u, m_uNumElements);
#endif //DXGL_FULL_EMULATION
ResetDontCareActionFlags();
}
STexture::~STexture()
{
// Confetti BEGIN: Igor Lobanchikov
if (m_pMapMemoryCopy)
{
MemalignFree(m_pMapMemoryCopy);
}
if (m_Texture)
{
[m_Texture release];
}
bool isDepthStencilTexSeparate = m_StencilTexture != m_Texture;
if (m_StencilTexture && isDepthStencilTexSeparate)
{
[m_StencilTexture release];
}
// Confetti End: Igor Lobanchikov
}
SShaderTextureViewPtr STexture::CreateShaderView(const SShaderTextureViewConfiguration& kConfiguration, CDevice* pDevice)
{
DXGL_TODO("This is not thread-safe, as multiple threads can create shader views for the same texture. Add synchronization primitive.");
SShaderTextureViewPtr spView(new SShaderTextureView(this, kConfiguration));
if (!spView->Init(pDevice))
{
return NULL;
}
return spView;
}
SShaderBufferViewPtr SBuffer::CreateShaderView(const SShaderBufferViewConfiguration& kConfiguration, CDevice* pDevice)
{
DXGL_TODO("This is not thread-safe, as multiple threads can create shader views for the same buffer. Add synchronization primitive.");
SShaderBufferViewPtr spView(new SShaderBufferView(this, kConfiguration));
if (!spView->Init(pDevice))
{
return NULL;
}
return spView;
}
SOutputMergerTextureViewPtr STexture::CreateOutputMergerView(const SOutputMergerTextureViewConfiguration& kConfiguration, CDevice* pDevice)
{
SOutputMergerTextureViewPtr spView(new SOutputMergerTextureView(this, kConfiguration));
if (!spView->Init(pDevice))
{
return NULL;
}
return spView;
}
SOutputMergerTextureViewPtr STexture::GetCompatibleOutputMergerView(const SOutputMergerTextureViewConfiguration& kConfiguration, CDevice* pDevice)
{
DXGL_TODO("This is not thread-safe, as multiple threads can create output merger views for the same texture. Add synchronization primitive.");
SOutputMergerTextureView* pExistingView(m_pOutputMergerViewsHead);
while (pExistingView != NULL)
{
if (pExistingView->m_kConfiguration == kConfiguration)
{
return pExistingView;
}
pExistingView = pExistingView->m_pNextView;
}
return CreateOutputMergerView(kConfiguration, pDevice);
}
void STexture::ResetDontCareActionFlags()
{
m_bColorLoadDontCare =
m_bDepthLoadDontCare =
m_bStencilLoadDontCare =
m_bColorStoreDontCare =
m_bDepthStoreDontCare =
m_bStencilStoreDontCare = false;
}
SShaderResourceView::SShaderResourceView(EGIFormat eFormat)
: m_eFormat(eFormat)
{
}
SShaderResourceView::~SShaderResourceView()
{
}
bool SShaderResourceView::GenerateMipmaps(CContext*)
{
DXGL_ERROR("Cannot create mipmaps from a generic shader resource view");
return false;
}
SShaderBufferView::SShaderBufferView(SBuffer* pBuffer, const SShaderBufferViewConfiguration& kConfiguration)
: SShaderResourceView(kConfiguration.m_eFormat)
, m_kConfiguration(kConfiguration)
, m_pBuffer(pBuffer)
, m_BufferView(0)
{
}
SShaderBufferView::~SShaderBufferView()
{
if (m_BufferView)
{
[m_BufferView release];
}
}
bool SShaderBufferView::Init(CDevice* pDevice)
{
if (!m_BufferView)
{
m_BufferView = GetMtlBufferBasedOnSize(m_pBuffer);
[m_BufferView retain];
}
return true;
}
id<MTLBuffer> SShaderBufferView::GetMetalBuffer()
{
id<MTLBuffer> mtlBuffer = GetMtlBufferBasedOnSize(m_pBuffer);
return m_BufferView ? m_BufferView : mtlBuffer;
}
bool SShaderBufferView::GenerateMipmaps(CContext* pContext)
{
DXGL_ERROR("Cannot create mipmaps from a buffer shader resource view");
return false;
}
SShaderTextureView::SShaderTextureView(STexture* pTexture, const SShaderTextureViewConfiguration& kConfiguration)
: SShaderResourceView(kConfiguration.m_eFormat)
, m_kConfiguration(kConfiguration)
, m_pTexture(pTexture)
// Confetti BEGIN: Igor Lobanchikov
, m_TextureView(0)
// Confetti End: Igor Lobanchikov
{
m_pNextView = pTexture->m_pShaderViewsHead;
pTexture->m_pShaderViewsHead = this;
}
SShaderTextureView::~SShaderTextureView()
{
// Confetti BEGIN: Igor Lobanchikov
if (m_TextureView)
{
[m_TextureView release];
}
// Confetti End: Igor Lobanchikov
SShaderTextureView** pLink = &m_pTexture->m_pShaderViewsHead;
while (*pLink != NULL)
{
if (*pLink == this)
{
*pLink = m_pNextView;
break;
}
pLink = &((*pLink)->m_pNextView);
}
}
bool SShaderTextureView::Init(CDevice* pDevice)
{
const SGIFormatInfo* pFormatInfo;
if (m_kConfiguration.m_eFormat == eGIF_NUM ||
(pFormatInfo = GetGIFormatInfo(m_kConfiguration.m_eFormat)) == NULL)
{
DXGL_ERROR("Invalid format for shader resource view");
return false;
}
if (m_kConfiguration.m_uMinMipLevel != 0
|| m_kConfiguration.m_uNumMipLevels != m_pTexture->m_uNumMipLevels)
{
DXGL_ERROR("Metal doesn't support SRV which map to a part of resource.");
}
bool bFormatRequiresUniqueView(false);
if (m_kConfiguration.m_eFormat != m_pTexture->m_eFormat)
{
if (pFormatInfo->m_eTypelessFormat != m_pTexture->m_eFormat)
{
DXGL_ERROR("Shader resource view format is not compatible with texture format");
return false;
}
switch (pFormatInfo->m_eTypelessConversion)
{
case eGIFC_DEPTH_TO_RED:
break;
case eGIFC_STENCIL_TO_RED:
#if defined AZ_PLATFORM_MAC
//Need a new texture view to access the stencil data. x32_stencil8 or x24_stencil8
bFormatRequiresUniqueView = true;
#else
m_TextureView = m_pTexture->m_StencilTexture;
[m_TextureView retain];
#endif
break;
case eGIFC_TEXTURE_VIEW:
bFormatRequiresUniqueView = true;
break;
case eGIFC_UNSUPPORTED:
DXGL_ERROR("Shader resource view conversion not supported for the requested format");
return false;
}
}
if (m_kConfiguration.m_eViewType != m_pTexture->m_eTextureType)
{
bFormatRequiresUniqueView = true;
}
if (bFormatRequiresUniqueView ||
m_kConfiguration.m_uMinLayer > 0 ||
m_kConfiguration.m_uNumLayers != m_pTexture->m_uNumElements)
{
// Confetti BEGIN: Igor Lobanchikov
assert(!m_TextureView);
assert(!m_pTexture->m_bBackBuffer);
if (m_pTexture->m_bBackBuffer)
{
DXGL_ERROR("Back buffer doesn't support views other than native format view");
return false;
}
// Confetti End: Igor Lobanchikov
if (!CreateUniqueView(pDevice))
{
return false;
}
}
// Confetti BEGIN: Igor Lobanchikov
if (!m_TextureView && !m_pTexture->m_bBackBuffer)
{
m_TextureView = m_pTexture->m_Texture;
[m_TextureView retain];
}
// Confetti End: Igor Lobanchikov
return true;
}
bool SShaderTextureView::CreateUniqueView(CDevice* pDevice)
{
const SGIFormatInfo* pFormatInfo(GetGIFormatInfo(m_kConfiguration.m_eFormat));
// Confetti BEGIN: Igor Lobanchikov
if (m_kConfiguration.m_uMinLayer > 0 ||
m_kConfiguration.m_uNumLayers != m_pTexture->m_uNumElements)
{
DXGL_ERROR("Not implemented. Metal doesn't support this functionality.");
}
if (pFormatInfo->m_eTypelessFormat != m_pTexture->m_eFormat)
{
DXGL_ERROR("Texture view format is not compatible with texture format");
return false;
}
m_TextureView = [m_pTexture->m_Texture newTextureViewWithPixelFormat:pFormatInfo->m_pTexture->m_eMetalFormat];
if (!m_TextureView)
{
DXGL_ERROR("Couldn't create output merger or depth-stencil view");
return false;
}
else
{
return true;
}
// Confetti End: Igor Lobanchikov
}
bool SShaderTextureView::GenerateMipmaps(CContext* pContext)
{
id<MTLBlitCommandEncoder> blitCommandEncoder = pContext->GetBlitCommandEncoder();
[blitCommandEncoder generateMipmapsForTexture: GetMetalTexture()];
return true;
}
SOutputMergerView::SOutputMergerView(EGIFormat eFormat)
: m_eFormat(eFormat)
{
}
SOutputMergerView::~SOutputMergerView()
{
}
const int32 SOutputMergerTextureView::INVALID_LAYER = -1;
SOutputMergerTextureView::SOutputMergerTextureView(STexture* pTexture, const SOutputMergerTextureViewConfiguration& kConfiguration)
: SOutputMergerView(kConfiguration.m_eFormat)
, m_kConfiguration(kConfiguration)
, m_pTexture(pTexture)
// Confetti BEGIN: Igor Lobanchikov
, m_RTView(0)
// Confetti End: Igor Lobanchikov
{
m_pNextView = pTexture->m_pOutputMergerViewsHead;
pTexture->m_pOutputMergerViewsHead = this;
}
SOutputMergerTextureView::~SOutputMergerTextureView()
{
SOutputMergerTextureView*& pLink = m_pTexture->m_pOutputMergerViewsHead;
while (pLink != NULL)
{
if (pLink == this)
{
pLink = m_pNextView;
}
else
{
pLink = pLink->m_pNextView;
}
}
// Confetti BEGIN: Igor Lobanchikov
if (m_RTView)
{
[m_RTView release];
}
// Confetti End: Igor Lobanchikov
}
// Confetti BEGIN: Igor Lobanchikov
bool IsMetalRenderable(MTLPixelFormat format)
{
switch (format)
{
#if !defined AZ_PLATFORM_MAC
case MTLPixelFormatB5G6R5Unorm:
case MTLPixelFormatR8Unorm_sRGB:
case MTLPixelFormatRG8Unorm_sRGB:
case MTLPixelFormatA1BGR5Unorm:
case MTLPixelFormatABGR4Unorm:
#endif
case MTLPixelFormatR8Unorm:
case MTLPixelFormatRG8Unorm:
case MTLPixelFormatRGBA8Unorm:
case MTLPixelFormatRG32Float:
case MTLPixelFormatRGBA32Float:
case MTLPixelFormatR32Float:
case MTLPixelFormatBGRA8Unorm:
case MTLPixelFormatRGBA8Unorm_sRGB:
case MTLPixelFormatBGRA8Unorm_sRGB:
case MTLPixelFormatR8Uint:
case MTLPixelFormatR8Sint:
case MTLPixelFormatRG8Uint:
case MTLPixelFormatRG8Sint:
case MTLPixelFormatRGBA8Uint:
case MTLPixelFormatRGBA8Sint:
case MTLPixelFormatR16Uint:
case MTLPixelFormatR16Sint:
case MTLPixelFormatRG16Uint:
case MTLPixelFormatRG16Sint:
case MTLPixelFormatRGBA16Uint:
case MTLPixelFormatRGBA16Sint:
case MTLPixelFormatR16Float:
case MTLPixelFormatRG16Float:
case MTLPixelFormatRGBA16Float:
case MTLPixelFormatR32Uint:
case MTLPixelFormatR32Sint:
case MTLPixelFormatRG32Uint:
case MTLPixelFormatRG32Sint:
case MTLPixelFormatRGBA32Uint:
case MTLPixelFormatRGBA32Sint:
case MTLPixelFormatRGB10A2Unorm:
case MTLPixelFormatRG11B10Float:
case MTLPixelFormatRGB9E5Float:
case MTLPixelFormatRGB10A2Uint:
case MTLPixelFormatRG8Snorm:
return true;
default:
return false;
}
}
bool IsMetalDepthRenderable(const SGIFormatInfo& formatInfo)
{
if (!formatInfo.m_pTexture)
{
return false;
}
bool isMetalDepthRenderable = formatInfo.m_pTexture->m_eMetalFormat == MTLPixelFormatDepth32Float ||
formatInfo.m_eTypelessFormat == eGIF_R32G8X24_TYPELESS || formatInfo.m_eTypelessFormat == eGIF_R16_TYPELESS ;
#if defined(AZ_PLATFORM_MAC)
isMetalDepthRenderable = isMetalDepthRenderable ||
formatInfo.m_pTexture->m_eMetalFormat == MTLPixelFormatDepth32Float_Stencil8 ||
formatInfo.m_pTexture->m_eMetalFormat == MTLPixelFormatDepth24Unorm_Stencil8;
#endif
return isMetalDepthRenderable;
}
// Confetti End: Igor Lobanchikov
bool SOutputMergerTextureView::Init(CDevice* pDevice)
{
m_iMipLevel = (GLint)m_kConfiguration.m_uMipLevel;
if (m_kConfiguration.m_uMinLayer == 0 && m_kConfiguration.m_uNumLayers == m_pTexture->m_uNumElements)
// Confetti BEGIN: Igor Lobanchikov
{
if (m_kConfiguration.m_uNumLayers != 1)
{
// Igor: for obvoiuse reasons iOS supports only 2D RT views, no arrays, no 3D RT, only a slice of it
DXGL_NOT_IMPLEMENTED;
}
else
{
m_iLayer = m_kConfiguration.m_uMinLayer;
}
}
// Confetti End: Igor Lobanchikov
else if (m_kConfiguration.m_uNumLayers == 1)
{
m_iLayer = m_kConfiguration.m_uMinLayer;
}
else
{
DXGL_NOT_IMPLEMENTED;
}
// Confetti BEGIN: Igor Lobanchikov
{
const SGIFormatInfo* pFormatInfo;
if (m_kConfiguration.m_eFormat == eGIF_NUM ||
(pFormatInfo = GetGIFormatInfo(m_kConfiguration.m_eFormat)) == NULL ||
pFormatInfo->m_pTexture == NULL ||
(!IsMetalRenderable(pFormatInfo->m_pTexture->m_eMetalFormat) && !IsMetalDepthRenderable(*pFormatInfo)))
{
DXGL_ERROR("Invalid format for output merger view");
return false;
}
assert(m_pTexture);
assert(m_pTexture->m_Texture);
if ((m_kConfiguration.m_eFormat != m_pTexture->m_eFormat) && !IsMetalDepthRenderable(*pFormatInfo))
{
assert(!m_pTexture->m_bBackBuffer);
if (m_pTexture->m_bBackBuffer)
{
DXGL_ERROR("Back buffer doesn't support views other than native format view");
return false;
}
return CreateUniqueView(pFormatInfo, pDevice);
}
else if (!m_pTexture->m_bBackBuffer)
{
m_RTView = m_pTexture->m_Texture;
[m_RTView retain];
}
return true;
}
// Confetti End: Igor Lobanchikov
if (m_kConfiguration.m_eFormat != m_pTexture->m_eFormat)
{
const SGIFormatInfo* pFormatInfo;
if (m_kConfiguration.m_eFormat == eGIF_NUM ||
(pFormatInfo = GetGIFormatInfo(m_kConfiguration.m_eFormat)) == NULL ||
pFormatInfo->m_pTexture == NULL)
{
DXGL_ERROR("Invalid format for output merger view");
return false;
}
if (pFormatInfo->m_pTexture->m_eMetalFormat == GetGIFormatInfo(m_pTexture->m_eFormat)->m_pTexture->m_eMetalFormat)
{
return true;
}
// Frame buffer attachment does not support any kind of in-place conversion - texture view is required unless no conversion is needed at all
return CreateUniqueView(pFormatInfo, pDevice);
}
return true;
}
bool SOutputMergerTextureView::CreateUniqueView(const SGIFormatInfo* pFormatInfo, CDevice* pDevice)
{
// Confetti BEGIN: Igor Lobanchikov
if (pFormatInfo->m_eTypelessFormat != m_pTexture->m_eFormat)
{
DXGL_ERROR("Output merger view format is not compatible with texture format");
return false;
}
m_RTView = [m_pTexture->m_Texture newTextureViewWithPixelFormat:pFormatInfo->m_pTexture->m_eMetalFormat];
if (!m_RTView)
{
DXGL_ERROR("Couldn't create output merger or depth-stencil view");
return false;
}
return true;
// Confetti End: Igor Lobanchikov
}
SBuffer::SBuffer()
: m_pSystemMemoryCopy(NULL)
, m_bMapped(false)
// Confetti BEGIN: Igor Lobanchikov
, m_BufferShared(0)
#if defined(AZ_PLATFORM_MAC)
, m_BufferManaged(0)
#endif
, m_pMappedData(0)
, m_uMapOffset(0)
, m_uMapSize(0)
, m_pfMapBufferRange(0)
// Confetti End: Igor Lobanchikov
{
}
SBuffer::~SBuffer()
{
if (m_pSystemMemoryCopy != NULL)
{
MemalignFree(m_pSystemMemoryCopy);
}
// Confetti BEGIN: Igor Lobanchikov
if (m_BufferShared)
{
[m_BufferShared release];
}
#if defined(AZ_PLATFORM_MAC)
if (m_BufferManaged)
{
[m_BufferManaged release];
}
#endif
// Confetti End: Igor Lobanchikov
}
bool SBuffer::GetBufferAndOffset(CContext& context, uint32 uInputBufferOffset, uint32 uBaseOffset, uint32 uBaseStride, id<MTLBuffer>& tmpBuffer, uint32& offset, bool const popTransientMappedDataQueue)
{
offset = uInputBufferOffset;
tmpBuffer = GetMtlBufferBasedOnSize(this);
void* tmpMappedData = m_pMappedData;
// Igor: for now vertex buffers always store data in Metal buffer
// Dynamic updates are expected to use direct CPU access
// Other updates are handled using GPU copies.
assert(tmpBuffer);
if (m_eUsage == eBU_MapInRingBufferTTLOnce)
{
assert(offset == uInputBufferOffset);
// We assume that tmpMappedData has already map offest applied. Don't want to do it twice.
offset = 0;
// David: set appropriate mapped data if the buffer was mapped multiple times
// Caller of this function is responsible to pop the m_pTransientMappedData list.
if (!m_pTransientMappedData.empty())
{
tmpMappedData = m_pTransientMappedData.front();
if (popTransientMappedDataQueue)
{
m_pTransientMappedData.pop_front();
}
}
}
// Igor: this is used to calculate offset correctly when ring buffer is used
if (tmpMappedData)
{
// Check if offset in this situation is always 0
assert(!offset);
offset += (uint8*)(tmpMappedData) - (uint8*)(tmpBuffer.contents);
}
{
offset += uBaseOffset * uBaseStride;
}
return true;
}
SQuery::~SQuery()
{
}
// Confetti BEGIN: Igor Lobanchikov
void SQuery::Begin(CContext* pContext)
{
}
void SQuery::End(CContext* pContext)
{
}
// Confetti End: Igor Lobanchikov
uint32 SQuery::GetDataSize()
{
return 0;
}
SPlainQuery::SPlainQuery()
{
}
SPlainQuery::~SPlainQuery()
{
}
// Confetti BEGIN: Igor Lobanchikov
void SPlainQuery::Begin(CContext* pContext)
{
DXMETAL_NOT_IMPLEMENTED
}
void SPlainQuery::End(CContext* pContext)
{
DXMETAL_NOT_IMPLEMENTED
}
SOcclusionQuery::SOcclusionQuery()
: m_pEventHelper(NULL)
, m_pQueryData(NULL)
{
}
void SOcclusionQuery::Begin(CContext* pContext)
{
assert(pContext);
m_pEventHelper = 0;
m_pQueryData = 0;
pContext->BeginOcclusionQuery(this);
}
void SOcclusionQuery::End(CContext* pContext)
{
pContext->EndOcclusionQuery(this);
}
bool SOcclusionQuery::GetData(void* pData, uint32 uDataSize, bool bFlush)
{
//TODO: m_pEventHelper is null sometimes
if (!m_pEventHelper)
{
return false;
}
//assert(m_pEventHelper != nullptr);
assert(m_pQueryData);
// Igor: If the command buffer is not submitted event will never be triggered.
// Flushing of the command buffer is expensive (resolve/restore all RTs bound)
// As the result we don't support flush operation.
// This will lead to stall, if the command buffer is not submitted and application loops until
// the event will be triggered which will never happen.
// Assert chacks for this situation. If you are here, it is a good idea to reconsider
// using the event the way you want to.
// note: when the application is just initialized frame throttle events will assert
// if there are no resources since command buffer has never been committed.
assert(!(bFlush) || (bFlush && m_pEventHelper->bCommandBufferSubmitted));
if (!(!(bFlush) || (bFlush && m_pEventHelper->bCommandBufferSubmitted)))
{
int i = 0;
++i;
AZ_TracePrintf("Metal", "Potential dead lock is possible! Event is not triggered. Continue to prevent a deadlock");
*reinterpret_cast<UINT64*>(pData) = 0;
return true;
}
DXMETAL_TODO("Consider returning something big for true instead of one for occlusion query.");
// Igor: Motivation: Cryengine uses threshold to determine visibility. 1 is usually below that threshold.
if (m_pEventHelper->bTriggered)
{
*reinterpret_cast<UINT64*>(pData) = *m_pQueryData;
return true;
}
else
{
return false;
}
}
// Confetti End: Igor Lobanchikov
uint32 SOcclusionQuery::GetDataSize()
{
return sizeof(UINT64);
}
// Confetti BEGIN: Igor Lobanchikov
SFenceSync::SFenceSync()
: m_pEventHelper(NULL)
{
}
SFenceSync::~SFenceSync()
{
}
void SFenceSync::End(CContext* pContext)
{
assert(pContext);
m_pEventHelper = pContext->GetCurrentEventHelper();
assert(m_pEventHelper);
}
bool SFenceSync::GetData(void* pData, uint32 uDataSize, bool bFlush)
{
assert(m_pEventHelper != NULL);
// Igor: If the command buffer is not submitted event will never be triggered.
// Flushing of the command buffer is expensive (resolve/restore all RTs bound)
// As the result we don't support flush operation.
// This will lead to stall, if the command buffer is not submitted and application loops until
// the event will be triggered which will never happen.
// Assert chacks for this situation. If you are here, it is a good idea to reconsider
// using the event the way you want to.
// note: when the application is just initialized frame throttle events will assert
// if there are no resources since command buffer has never been committed.
assert(!(bFlush) || (bFlush && m_pEventHelper->bCommandBufferSubmitted));
if (!(!(bFlush) || (bFlush && m_pEventHelper->bCommandBufferSubmitted)))
{
int i = 0;
++i;
AZ_TracePrintf("Metal", "Potential dead lock is possible! Event is not triggered. Continue to prevent a deadlock");
*reinterpret_cast<BOOL*>(pData) = TRUE;
return true;
}
if (m_pEventHelper->bTriggered)
{
*reinterpret_cast<BOOL*>(pData) = TRUE;
return true;
}
else
{
return false;
}
}
// Confetti End: Igor Lobanchikov
uint32 SFenceSync::GetDataSize()
{
return sizeof(BOOL);
}
struct SDefaultFrameBufferOutputMergerView
: SOutputMergerTextureView
{
SDefaultFrameBufferOutputMergerView(SDefaultFrameBufferTexture* pTexture, const SOutputMergerTextureViewConfiguration& kConfiguration)
: SOutputMergerTextureView(pTexture, kConfiguration)
{
}
virtual bool CreateUniqueView(const SGIFormatInfo* pFormatInfo, CDevice* pDevice)
{
return SOutputMergerTextureView::CreateUniqueView(pFormatInfo, 0);
}
};
struct SDefaultFrameBufferShaderView
: SShaderTextureView
{
SDefaultFrameBufferShaderView(SDefaultFrameBufferTexture* pTexture, const SShaderTextureViewConfiguration& kConfiguration)
: SShaderTextureView(pTexture, kConfiguration)
{
}
};
SDefaultFrameBufferTexture::SDefaultFrameBufferTexture(int32 iWidth, int32 iHeight, EGIFormat eFormat)
: STexture(iWidth, iHeight, 1, MTLTextureType2D, eFormat, 1, 1)
#if CRY_DXGL_FULL_EMULATION
, m_kCustomWindowContext(NULL)
#endif //DXGL_FULL_EMULATION
{
m_pfUpdateSubresource = &UpdateSubresource;
m_pfMapSubresource = &MapSubresource;
m_pfUnmapSubresource = &UnmapSubresource;
}
SShaderTextureViewPtr SDefaultFrameBufferTexture::CreateShaderView(const SShaderTextureViewConfiguration& kConfiguration, CDevice* pDevice)
{
SShaderTextureViewPtr spView(new SDefaultFrameBufferShaderView(this, kConfiguration));
if (!spView->Init(pDevice))
{
return NULL;
}
return spView;
}
SOutputMergerTextureViewPtr SDefaultFrameBufferTexture::CreateOutputMergerView(const SOutputMergerTextureViewConfiguration& kConfiguration, CDevice* pDevice)
{
SOutputMergerTextureViewPtr spView(new SDefaultFrameBufferOutputMergerView(this, kConfiguration));
if (!spView->Init(pDevice))
{
return NULL;
}
return spView;
}
#if DXGL_FULL_EMULATION
void SDefaultFrameBufferTexture::SetCustomWindowContext(const TWindowContext& kCustomWindowContext)
{
m_kCustomWindowContext = kCustomWindowContext;
}
#endif //DXGL_FULL_EMULATION
void SDefaultFrameBufferTexture::UpdateSubresource(SResource* pResource, uint32 uSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, uint32 uSrcRowPitch, uint32 uSrcDepthPitch, CContext* pContext)
{
if (uSubresource > 0)
{
DXGL_ERROR("The only valid subresource index for the default frame buffer is 0 - cannot update subresource");
return;
}
SDefaultFrameBufferTexture* pDefaultFramebufferTexture(static_cast<SDefaultFrameBufferTexture*>(pResource));
UpdateTexSubresource<SSingleTexImpl<STex2DUncompressed> >(pDefaultFramebufferTexture, uSubresource, pDstBox, pSrcData, uSrcRowPitch, uSrcDepthPitch, pContext);
}
bool SDefaultFrameBufferTexture::MapSubresource(SResource* pResource, uint32 uSubresource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource, CContext* pContext)
{
if (uSubresource > 0)
{
DXGL_ERROR("The only valid subresource index for the default frame buffer is 0 - cannot map subresource");
return false;
}
SDefaultFrameBufferTexture* pDefaultFramebufferTexture(static_cast<SDefaultFrameBufferTexture*>(pResource));
switch (MapType)
{
case D3D11_MAP_READ:
case D3D11_MAP_READ_WRITE:
case D3D11_MAP_WRITE:
break;
default:
DXGL_ERROR("Unsupported map operation type for default frame buffer");
return false;
}
return MapTexSubresource<SSingleTexImpl<STex2DUncompressed> >(pDefaultFramebufferTexture, uSubresource, MapType, MapFlags, pMappedResource, pContext);
}
void SDefaultFrameBufferTexture::UnmapSubresource(SResource* pResource, uint32 uSubresource, CContext* pContext)
{
if (uSubresource > 0)
{
DXGL_ERROR("The only valid subresource index for the default frame buffer is 0 - cannot unmap subresource");
return;
}
SDefaultFrameBufferTexture* pDefaultFramebufferTexture(static_cast<SDefaultFrameBufferTexture*>(pResource));
UnmapTexSubresource<SSingleTexImpl<STex2DUncompressed> >(pDefaultFramebufferTexture, uSubresource, pContext);
}
const SGIFormatInfo* GetCompatibleTextureFormatInfo(EGIFormat* peGIFormat)
{
const SGIFormatInfo* pFormatInfo(GetGIFormatInfo(*peGIFormat));
if (pFormatInfo->m_pTexture != NULL)
{
return pFormatInfo;
}
if (pFormatInfo->m_eTypelessFormat != eGIF_NUM && pFormatInfo->m_eTypelessFormat != *peGIFormat)
{
*peGIFormat = pFormatInfo->m_eTypelessFormat;
pFormatInfo = GetGIFormatInfo(pFormatInfo->m_eTypelessFormat);
if (pFormatInfo->m_pTexture != NULL)
{
return pFormatInfo;
}
}
*peGIFormat = eGIF_NUM;
return NULL;
}
STexturePtr CreateTexture1D(const D3D11_TEXTURE1D_DESC& kDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, CDevice* pDevice /*CContext* pContext*/)
{
DXGL_SCOPED_PROFILE("CreateTexture1D")
EGIFormat eGIFormat(GetGIFormat(kDesc.Format));
const SGIFormatInfo* pFormatInfo(NULL);
if (eGIFormat == eGIF_NUM ||
(pFormatInfo = GetCompatibleTextureFormatInfo(&eGIFormat)) == NULL)
{
DXGL_ERROR("Invalid format for 1D texture");
return NULL;
}
uint32 uNumElements(kDesc.ArraySize);
bool bArray(kDesc.ArraySize > 1 || (kDesc.MiscFlags & D3D11_RESOURCE_MISC_DXGL_FORCE_ARRAY));
STexturePtr spTexture(
new STexture(
kDesc.Width, 1, 1,
bArray ? MTLTextureType1DArray : MTLTextureType1D,
eGIFormat, GetNumMipLevels(kDesc), kDesc.ArraySize));
if (kDesc.Usage == D3D11_USAGE_STAGING)
{
if (pFormatInfo->m_pTexture->m_bCompressed)
{
InitializeTexture<SStagingTexImpl<STex1DCompressed> >(spTexture, pInitialData, kDesc.CPUAccessFlags, pDevice, pFormatInfo, kDesc.BindFlags);
}
else
{
InitializeTexture<SStagingTexImpl<STex1DUncompressed> >(spTexture, pInitialData, kDesc.CPUAccessFlags, pDevice, pFormatInfo, kDesc.BindFlags);
}
}
else
{
if (pFormatInfo->m_pTexture->m_bCompressed)
{
if (bArray)
{
InitializeTexture<SArrayTexImpl<STex2DCompressed> >(spTexture, pInitialData, kDesc.CPUAccessFlags, pDevice, pFormatInfo, kDesc.BindFlags);
}
else
{
InitializeTexture<SSingleTexImpl<STex1DCompressed> >(spTexture, pInitialData, kDesc.CPUAccessFlags, pDevice, pFormatInfo, kDesc.BindFlags);
}
}
else
{
if (bArray)
{
InitializeTexture<SArrayTexImpl<STex2DUncompressed> >(spTexture, pInitialData, kDesc.CPUAccessFlags, pDevice, pFormatInfo, kDesc.BindFlags);
}
else
{
InitializeTexture<SSingleTexImpl<STex1DUncompressed> >(spTexture, pInitialData, kDesc.CPUAccessFlags, pDevice, pFormatInfo, kDesc.BindFlags);
}
}
}
return spTexture;
}
void InitializeTexture2D(STexture* pTexture, bool bArray, bool bStaging, const D3D11_SUBRESOURCE_DATA* pInitialData, uint32 uCPUAccess, CDevice* pDevice /*CContext* pContext*/, const SGIFormatInfo* pFormatInfo, const uint32 uBindFlags)
{
if (bStaging)
{
if (pFormatInfo->m_pTexture->m_bCompressed)
{
InitializeTexture<SStagingTexImpl<STex2DCompressed> >(pTexture, pInitialData, uCPUAccess, pDevice, pFormatInfo, uBindFlags);
}
else
{
InitializeTexture<SStagingTexImpl<STex2DUncompressed> >(pTexture, pInitialData, uCPUAccess, pDevice, pFormatInfo, uBindFlags);
}
}
else
{
if (pFormatInfo->m_pTexture->m_bCompressed)
{
if (bArray)
{
InitializeTexture<SArrayTexImpl<STex3DCompressed> >(pTexture, pInitialData, uCPUAccess, pDevice, pFormatInfo, uBindFlags);
}
else
{
InitializeTexture<SSingleTexImpl<STex2DCompressed> >(pTexture, pInitialData, uCPUAccess, pDevice, pFormatInfo, uBindFlags);
}
}
else
{
if (bArray)
{
InitializeTexture<SArrayTexImpl<STex3DUncompressed> >(pTexture, pInitialData, uCPUAccess, pDevice, pFormatInfo, uBindFlags);
}
else
{
InitializeTexture<SSingleTexImpl<STex2DUncompressed> >(pTexture, pInitialData, uCPUAccess, pDevice, pFormatInfo, uBindFlags);
}
}
}
}
STexturePtr CreateTexture2D(const D3D11_TEXTURE2D_DESC& kDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, CDevice* pDevice /*CContext* pContext*/)
{
DXGL_SCOPED_PROFILE("CreateTexture2D")
EGIFormat eGIFormat(GetGIFormat(kDesc.Format));
const SGIFormatInfo* pFormatInfo(NULL);
if (eGIFormat == eGIF_NUM ||
(pFormatInfo = GetCompatibleTextureFormatInfo(&eGIFormat)) == NULL)
{
DXGL_ERROR("Invalid format for 2D texture");
return NULL;
}
bool bStaging(kDesc.Usage == D3D11_USAGE_STAGING);
if (kDesc.SampleDesc.Count > 1)
{
DXGL_NOT_IMPLEMENTED
return NULL;
}
else
{
if ((kDesc.MiscFlags & D3D11_RESOURCE_MISC_TEXTURECUBE) != 0)
{
bool bArray(kDesc.ArraySize > 6 || (kDesc.MiscFlags & D3D11_RESOURCE_MISC_DXGL_FORCE_ARRAY));
if (bArray)
{
DXGL_NOT_IMPLEMENTED;
return NULL;
}
STexturePtr spTexture(
new STexture(
kDesc.Width, kDesc.Height, 1,
MTLTextureTypeCube,
eGIFormat, GetNumMipLevels(kDesc), kDesc.ArraySize));
InitializeTexture2D(spTexture, bArray, bStaging, pInitialData, kDesc.CPUAccessFlags, pDevice, pFormatInfo, kDesc.BindFlags);
return spTexture;
}
else
{
bool bArray(kDesc.ArraySize > 1 || (kDesc.MiscFlags & D3D11_RESOURCE_MISC_DXGL_FORCE_ARRAY));
STexturePtr spTexture(
new STexture(
kDesc.Width, kDesc.Height, 1,
bArray ? MTLTextureType2DArray : MTLTextureType2D,
eGIFormat, GetNumMipLevels(kDesc), kDesc.ArraySize));
InitializeTexture2D(spTexture, bArray, bStaging, pInitialData, kDesc.CPUAccessFlags, pDevice, pFormatInfo, kDesc.BindFlags);
return spTexture;
}
}
}
STexturePtr CreateTexture3D(const D3D11_TEXTURE3D_DESC& kDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, CDevice* pDevice /*CContext* pContext*/)
{
DXGL_SCOPED_PROFILE("CreateTexture3D")
EGIFormat eGIFormat(GetGIFormat(kDesc.Format));
const SGIFormatInfo* pFormatInfo(NULL);
if (eGIFormat == eGIF_NUM ||
(pFormatInfo = GetCompatibleTextureFormatInfo(&eGIFormat)) == NULL)
{
DXGL_ERROR("Invalid format for 3D texture");
return NULL;
}
STexturePtr spTexture(
new STexture(
kDesc.Width, kDesc.Height, kDesc.Depth,
MTLTextureType3D,
eGIFormat, GetNumMipLevels(kDesc), 1));
if (kDesc.Usage == D3D11_USAGE_STAGING)
{
if (pFormatInfo->m_pTexture->m_bCompressed)
{
InitializeTexture<SStagingTexImpl<STex3DCompressed> >(spTexture, pInitialData, kDesc.CPUAccessFlags, pDevice, pFormatInfo, kDesc.BindFlags);
}
else
{
InitializeTexture<SStagingTexImpl<STex3DUncompressed> >(spTexture, pInitialData, kDesc.CPUAccessFlags, pDevice, pFormatInfo, kDesc.BindFlags);
}
}
else
{
if (pFormatInfo->m_pTexture->m_bCompressed)
{
InitializeTexture<SSingleTexImpl<STex3DCompressed> >(spTexture, pInitialData, kDesc.CPUAccessFlags, pDevice, pFormatInfo, kDesc.BindFlags);
}
else
{
InitializeTexture<SSingleTexImpl<STex3DUncompressed> >(spTexture, pInitialData, kDesc.CPUAccessFlags, pDevice, pFormatInfo, kDesc.BindFlags);
}
}
return spTexture;
}
void UpdateRingBuffer(SBuffer* pBuffer, CContext* pContext, bool checkIfBufferIsMapped)
{
MemRingBufferStorage memAllocMode = GetMemAllocModeBasedOnSize(pBuffer->m_uMapSize);
id<MTLBuffer> mtlBuffer = GetMtlBufferBasedOnSize(pBuffer);
assert(pBuffer->m_BufferShared);
bool checkForMapping = checkIfBufferIsMapped ? pBuffer->m_bMapped : true;
// Igor: pBuffer->m_bMapped is checked here because we don't want to do anything if the buffer is not mapped.
if ((pBuffer->m_eUsage == eBU_Default) && checkForMapping)
{
id<MTLBuffer> tmpBuffer = pContext->GetRingBuffer(memAllocMode);
size_t unusedOffset = 0;
void* pTempData = pContext->AllocateMemoryInRingBuffer(pBuffer->m_uMapSize, memAllocMode, unusedOffset);
size_t tmpOffset = (uint8*)pTempData - (uint8*)tmpBuffer.contents;
cryMemcpy(pTempData, pBuffer->m_pSystemMemoryCopy + pBuffer->m_uMapOffset, pBuffer->m_uMapSize);
id<MTLBlitCommandEncoder> blitCommandEncoder = pContext->GetBlitCommandEncoder();
[blitCommandEncoder copyFromBuffer: tmpBuffer
sourceOffset: tmpOffset
toBuffer: mtlBuffer
destinationOffset: pBuffer->m_uMapOffset
size: pBuffer->m_uMapSize];
pBuffer->m_uMapOffset = 0;
pBuffer->m_uMapSize = 0;
}
#if defined(AZ_PLATFORM_MAC)
else
{
//If this buffer was using the faster ring buffer synchronize with the GPU
if (memAllocMode == MEM_MANAGED_RINGBUFFER)
{
[pBuffer->m_BufferManaged didModifyRange: NSMakeRange(pBuffer->m_uMapOffset, pBuffer->m_uMapSize)];
}
}
#endif
}
struct SDefaultBufferImpl
{
static void UpdateBufferSubresource(SResource* pResource, uint32 uSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, uint32, uint32, CContext* pContext)
{
DXGL_SCOPED_PROFILE("SDefaultBufferImpl::UpdateBufferSubresource")
SBuffer * pBuffer(static_cast<SBuffer*>(pResource));
assert(pBuffer->m_BufferShared);
size_t ringBufferOffsetOut = 0;
id<MTLBuffer> tmpBuffer = pContext->GetRingBuffer(MEM_SHARED_RINGBUFFER);
void* pTempData = pContext->AllocateMemoryInRingBuffer(pBuffer->m_uSize, MEM_SHARED_RINGBUFFER, ringBufferOffsetOut);
size_t tmpOffset = (uint8*)pTempData - (uint8*)tmpBuffer.contents;
cryMemcpy(pTempData, pSrcData, pDstBox ? pDstBox->right - pDstBox->left : pBuffer->m_BufferShared.length);
id<MTLBlitCommandEncoder> blitCommandEncoder = pContext->GetBlitCommandEncoder();
[blitCommandEncoder copyFromBuffer: tmpBuffer
sourceOffset: tmpOffset
toBuffer: pBuffer->m_BufferShared
destinationOffset: pDstBox ? pDstBox->left : 0
size: pDstBox ? pDstBox->right - pDstBox->left : pBuffer->m_BufferShared.length];
}
};
struct SDynamicBufferImpl
{
static bool MapBufferRange(SBuffer* pBuffer, size_t uOffset, size_t uSize, D3D11_MAP eMapType, uint32 uMapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource, CContext* pContext)
{
DXGL_SCOPED_PROFILE("SDynamicBufferImpl::MapBufferRange")
// Confetti BEGIN: Igor Lobanchikov
size_t ringBufferOffsetOut = 0;
DXMETAL_TODO("Add more buffer usage types for optimization purposes.");
// Igor: Use local copy now, copy it to ring buffer when rendering the actual geometry
// Note: vertex buffer must be 256 bytes aligned when rendering.
// Igor: eBU_MapInRingBuffer is set for constant buffer. So use ring buffer for constant buffer updates
switch (pBuffer->m_eUsage)
{
case eBU_Default:
{
assert(pBuffer->m_pSystemMemoryCopy);
pMappedResource->pData = pBuffer->m_pSystemMemoryCopy + uOffset;
assert(pBuffer->m_BufferShared);
pBuffer->m_bMapped = true;
break;
}
case eBU_MapInRingBufferTTLFrame:
{
MemRingBufferStorage memAllocMode = GetMemAllocModeBasedOnSize(uSize);
if (!pBuffer->m_BufferShared)
{
pBuffer->m_BufferShared = pContext->GetRingBuffer(MEM_SHARED_RINGBUFFER);
[pBuffer->m_BufferShared retain];
}
#if defined(AZ_PLATFORM_MAC)
if (!pBuffer->m_BufferManaged)
{
pBuffer->m_BufferManaged = pContext->GetRingBuffer(MEM_MANAGED_RINGBUFFER);
[pBuffer->m_BufferManaged retain];
}
#endif
pBuffer->m_bMapped = true;
if (eMapType == D3D11_MAP_WRITE_NO_OVERWRITE)
{
if (!pBuffer->m_pMappedData)
{
pBuffer->m_pMappedData = pContext->AllocateMemoryInRingBuffer(pBuffer->m_uSize, memAllocMode, ringBufferOffsetOut);
}
}
else
{
// The only other possible mode for dynamic buffers is D3D11_MAP_WRITE_DISCARD
assert(eMapType == D3D11_MAP_WRITE_DISCARD);
pBuffer->m_pMappedData = pContext->AllocateMemoryInRingBuffer(pBuffer->m_uSize, memAllocMode, ringBufferOffsetOut);
}
pMappedResource->pData = (uint8*)pBuffer->m_pMappedData;
break;
}
default:
DXMETAL_NOT_IMPLEMENTED;
}
// Confetti End: Igor Lobanchikov
pMappedResource->RowPitch = 0; // Meaningless for buffers
pMappedResource->DepthPitch = 0; // Meaningless for buffers
pBuffer->m_uMapOffset = ringBufferOffsetOut; // This is needed to synchronize with the GPU (didModifyRange)
pBuffer->m_uMapSize = uSize;
return true;
}
static bool MapBuffer(SResource* pResource, uint32 uSubresource, D3D11_MAP eMapType, uint32 uMapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource, CContext* pContext)
{
DXGL_SCOPED_PROFILE("SDynamicBufferImpl::MapBuffer")
SBuffer * pBuffer(static_cast<SBuffer*>(pResource));
return MapBufferRange(pBuffer, (size_t)0, (size_t)pBuffer->m_uSize, eMapType, uMapFlags, pMappedResource, pContext);
}
static void UnmapBuffer(SResource* pResource, UINT Subresource, CContext* pContext)
{
DXGL_SCOPED_PROFILE("SDynamicBufferImpl::UnmapBuffer")
SBuffer * pBuffer(static_cast<SBuffer*>(pResource));
//Update the ring buffer with the final rendering data
bool checkIfBufferIsMapped = true;//Check to make sure this buffer is mapped if it is eBU_Default
UpdateRingBuffer(pBuffer, pContext, checkIfBufferIsMapped);
pBuffer->m_bMapped = false;
}
static void UpdateBufferSubresource(SResource* pResource, uint32 uSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, uint32, uint32, CContext* pContext)
{
DXGL_SCOPED_PROFILE("SDynamicBufferImpl::UpdateBufferSubresource")
SBuffer * pBuffer(static_cast<SBuffer*>(pResource));
assert(uSubresource == 0);
uint32 uDstOffset, uDstSize;
if (pDstBox != NULL)
{
uDstOffset = pDstBox->left;
uDstSize = pDstBox->right - pDstBox->left;
}
else
{
uDstOffset = 0;
uDstSize = pBuffer->m_uSize;
}
//Update the resource
if (pBuffer->m_pSystemMemoryCopy)
{
cryMemcpy(pBuffer->m_pSystemMemoryCopy + uDstOffset, static_cast<const uint8*>(pSrcData), uDstSize);
}
assert(pBuffer->m_eUsage == eBU_Default);
assert(pBuffer->m_BufferShared);
//Update the ring buffer with the final rendering data
bool checkIfBufferIsMapped = false;//Check to make sure this buffer is mapped if it is eBU_Default
UpdateRingBuffer(pBuffer, pContext, checkIfBufferIsMapped);
}
};
struct SStagingBufferImpl
{
static bool MapBufferRange(SBuffer* pBuffer, size_t uOffset, size_t uSize, D3D11_MAP, uint32, D3D11_MAPPED_SUBRESOURCE* pMappedResource, CContext*)
{
DXGL_SCOPED_PROFILE("SStagingBufferImpl::MapBufferRange")
// Confetti BEGIN: Igor Lobanchikov
pMappedResource->pData = pBuffer->m_pSystemMemoryCopy + uOffset;
// Confetti End: Igor Lobanchikov
pMappedResource->RowPitch = 0; // Meaningless for buffers
pMappedResource->DepthPitch = 0; // Meaningless for buffers
return true;
}
static bool MapBuffer(SResource* pResource, uint32, D3D11_MAP, uint32, D3D11_MAPPED_SUBRESOURCE* pMappedResource, CContext*)
{
DXGL_SCOPED_PROFILE("SStagingBufferImpl::MapBuffer")
SBuffer * pBuffer(static_cast<SBuffer*>(pResource));
pMappedResource->pData = pBuffer->m_pSystemMemoryCopy;
// Confetti End: Igor Lobanchikov
pMappedResource->RowPitch = 0; // Meaningless for buffers
pMappedResource->DepthPitch = 0; // Meaningless for buffers
return true;
}
static void UnmapBuffer(SResource*, UINT, CContext*)
{
}
static void UpdateBufferSubresource(SResource* pResource, uint32 uSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, uint32, uint32, CContext*)
{
DXGL_SCOPED_PROFILE("SStagingBufferImpl::UpdateBufferSubresource")
SBuffer * pBuffer(static_cast<SBuffer*>(pResource));
assert(uSubresource == 0);
// Confetti BEGIN: Igor Lobanchikov
if (pDstBox != NULL)
{
cryMemcpy(pBuffer->m_pSystemMemoryCopy + pDstBox->left, pSrcData, pDstBox->right - pDstBox->left);
}
else
{
cryMemcpy(pBuffer->m_pSystemMemoryCopy, pSrcData, pBuffer->m_uSize);
}
// Confetti End: Igor Lobanchikov
}
};
struct STransientBufferImpl
{
static bool MapBufferRange(SBuffer* pBuffer, size_t uOffset, size_t uSize, D3D11_MAP eMapType, uint32 uMapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource, CContext* pContext)
{
DXGL_SCOPED_PROFILE("STransientBufferImpl::MapBufferRange")
assert(pBuffer->m_eUsage == eBU_MapInRingBufferTTLOnce);
assert(!pBuffer->m_pSystemMemoryCopy);
size_t ringBufferOffsetOut = 0;
MemRingBufferStorage memAllocMode = GetMemAllocModeBasedOnSize(uSize);
if (!pBuffer->m_BufferShared)
{
pBuffer->m_BufferShared = pContext->GetRingBuffer(MEM_SHARED_RINGBUFFER);
[pBuffer->m_BufferShared retain];
}
#if defined(AZ_PLATFORM_MAC)
if (!pBuffer->m_BufferManaged)
{
pBuffer->m_BufferManaged = pContext->GetRingBuffer(MEM_MANAGED_RINGBUFFER);
[pBuffer->m_BufferManaged retain];
}
#endif
pBuffer->m_bMapped = true;
if (eMapType == D3D11_MAP_WRITE_NO_OVERWRITE)
{
assert(uOffset + uSize <= pBuffer->m_uSize);
pBuffer->m_pMappedData = pContext->AllocateMemoryInRingBuffer(uSize, memAllocMode, ringBufferOffsetOut);
}
else
{
// The only other possible mode for dynamic buffers is D3D11_MAP_WRITE_DISCARD
assert(eMapType == D3D11_MAP_WRITE_DISCARD);
assert(uSize <= pBuffer->m_uSize);
pBuffer->m_pMappedData = pContext->AllocateMemoryInRingBuffer(uSize, memAllocMode, ringBufferOffsetOut);
}
pMappedResource->pData = (uint8*)pBuffer->m_pMappedData;
pMappedResource->RowPitch = 0; // Meaningless for buffers
pMappedResource->DepthPitch = 0; // Meaningless for buffers
pBuffer->m_uMapOffset = ringBufferOffsetOut; // This is needed to synchronize with the GPU (didModifyRange)
pBuffer->m_uMapSize = uSize;
pBuffer->m_pTransientMappedData.push_back(pBuffer->m_pMappedData);
return true;
}
static void UnmapBuffer(SResource* pResource, UINT Subresource, CContext* pContext)
{
DXGL_SCOPED_PROFILE("STransientBufferImpl::UnmapBuffer")
SBuffer * pBuffer(static_cast<SBuffer*>(pResource));
assert(pBuffer->m_eUsage == eBU_MapInRingBufferTTLOnce);
#if defined(AZ_PLATFORM_MAC)
//If this buffer was using the faster ring buffer synchronize with the GPU
MemRingBufferStorage memAllocMode = GetMemAllocModeBasedOnSize(pBuffer->m_uMapSize);
if (memAllocMode == MEM_MANAGED_RINGBUFFER)
{
[pBuffer->m_BufferManaged didModifyRange: NSMakeRange(pBuffer->m_uMapOffset, pBuffer->m_uMapSize)];
}
#endif
pBuffer->m_bMapped = false;
}
};
struct SDirectAccessBufferImpl
{
static bool MapBufferRange(SBuffer* pBuffer, size_t uOffset, size_t uSize, D3D11_MAP, uint32, D3D11_MAPPED_SUBRESOURCE* pMappedResource, CContext*)
{
DXGL_SCOPED_PROFILE("SDirectAccessBufferImpl::MapBufferRange")
assert(pBuffer->m_BufferShared);
assert(pBuffer->m_eUsage == eBU_DirectAccess);
//DirectAccess buffers allocate from shared memory only.
pMappedResource->pData = (uint8*)pBuffer->m_BufferShared.contents + uOffset;
pMappedResource->RowPitch = 0; // Meaningless for buffers
pMappedResource->DepthPitch = 0; // Meaningless for buffers
return true;
}
static bool MapBuffer(SResource* pResource, uint32, D3D11_MAP, uint32, D3D11_MAPPED_SUBRESOURCE* pMappedResource, CContext*)
{
DXGL_SCOPED_PROFILE("SDirectAccessBufferImpl::MapBuffer")
SBuffer * pBuffer(static_cast<SBuffer*>(pResource));
assert(pBuffer->m_BufferShared);
assert(pBuffer->m_eUsage == eBU_DirectAccess);
//DirectAccess buffers allocate from shared memory only.
pMappedResource->pData = (uint8*)pBuffer->m_BufferShared.contents;
pMappedResource->RowPitch = 0; // Meaningless for buffers
pMappedResource->DepthPitch = 0; // Meaningless for buffers
return true;
}
static void UnmapBuffer(SResource*, UINT, CContext*)
{
}
};
SBufferPtr CreateBuffer(const D3D11_BUFFER_DESC& kDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, CDevice* pDevice /*CContext* pContext*/)
{
DXGL_SCOPED_PROFILE("CreateBuffer")
SBufferPtr spBuffer(new SBuffer());
spBuffer->m_uSize = kDesc.ByteWidth;
for (UINT uBindMask = 1; uBindMask != 0; uBindMask <<= 1)
{
switch (kDesc.BindFlags & uBindMask)
{
case 0:
break;
case D3D11_BIND_VERTEX_BUFFER:
spBuffer->m_kBindings.Set(eBB_Array, true);
break;
case D3D11_BIND_INDEX_BUFFER:
spBuffer->m_kBindings.Set(eBB_ElementArray, true);
break;
case D3D11_BIND_CONSTANT_BUFFER:
spBuffer->m_kBindings.Set(eBB_UniformBuffer, true);
break;
#if DXGL_SUPPORT_TEXTURE_BUFFERS
case D3D11_BIND_SHADER_RESOURCE:
spBuffer->m_kBindings.Set(eBB_Texture, true);
break;
#endif //DXGL_SUPPORT_TEXTURE_BUFFERS
case D3D11_BIND_UNORDERED_ACCESS:
if ((kDesc.MiscFlags & D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) != 0)
{
#if DXGL_SUPPORT_SHADER_STORAGE_BLOCKS
spBuffer->m_kBindings.Set(eBB_ShaderStorage, true);
break;
#endif //DXGL_SUPPORT_SHADER_STORAGE_BLOCKS
}
else
{
#if DXGL_SUPPORT_TEXTURE_BUFFERS
spBuffer->m_kBindings.Set(eBB_Texture, true);
break;
#endif //DXGL_SUPPORT_TEXTURE_BUFFERS
}
default:
DXGL_TODO("Support more buffer bindings");
DXGL_ERROR("Buffer binding not supported");
return NULL;
}
}
bool bVideoMemory = false, bAllocateSystemMemory = false;
switch (kDesc.Usage)
{
case D3D11_USAGE_DEFAULT:
spBuffer->m_pfUpdateSubresource = &SDefaultBufferImpl::UpdateBufferSubresource;
case D3D11_USAGE_IMMUTABLE:
if ((kDesc.CPUAccessFlags & D3D11_CPU_ACCESS_WRITE) == 0)
{
spBuffer->m_eUsage = eBU_Default;//GL_STATIC_READ;
}
else if ((kDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ) == 0)
{
spBuffer->m_eUsage = eBU_Default;//GL_STATIC_DRAW;
}
else
{
spBuffer->m_eUsage = eBU_Default;//GL_STATIC_COPY;
}
bVideoMemory = true;
bAllocateSystemMemory = false;
break;
case D3D11_USAGE_DYNAMIC:
spBuffer->m_pfMapSubresource = &SDynamicBufferImpl::MapBuffer;
spBuffer->m_pfUnmapSubresource = &SDynamicBufferImpl::UnmapBuffer;
spBuffer->m_pfUpdateSubresource = &SDynamicBufferImpl::UpdateBufferSubresource;
spBuffer->m_pfMapBufferRange = &SDynamicBufferImpl::MapBufferRange;
if ((kDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ) != 0)
{
DXGL_ERROR("Cannot create a buffer with dynamic usage that is CPU readable");
return NULL;
}
if ((kDesc.BindFlags & D3D11_BIND_CONSTANT_BUFFER) != 0)
{
// Assuming that constant buffers are accessed more frequently and usually discarded on updates
spBuffer->m_eUsage = eBU_MapInRingBufferTTLFrame;//GL_DYNAMIC_DRAW;
}
else
{
spBuffer->m_eUsage = eBU_Default;//GL_STREAM_DRAW;
}
// Confetti BEGIN: Igor Lobanchikov
if (spBuffer->m_eUsage == eBU_Default)
{
bVideoMemory = true;
// Igor: we always map the system memory, then copy to ring buffer and use GPU to copy from ring buffer to the actual buffer.
// Slow but safe.
// This path should never be used. Consider using other approaches.
bAllocateSystemMemory = true;
}
else
{
bVideoMemory = false;
bAllocateSystemMemory = false;
}
// Confetti End: Igor Lobanchikov
break;
case D3D11_USAGE_STAGING:
spBuffer->m_pfMapSubresource = &SStagingBufferImpl::MapBuffer;
spBuffer->m_pfUnmapSubresource = &SStagingBufferImpl::UnmapBuffer;
spBuffer->m_pfUpdateSubresource = &SStagingBufferImpl::UpdateBufferSubresource;
spBuffer->m_pfMapBufferRange = &SStagingBufferImpl::MapBufferRange;
spBuffer->m_eUsage = eBU_Default;//GL_NONE;
AZ_Assert(!(kDesc.CPUAccessFlags & D3D11_CPU_ACCESS_WRITE), "The resource should not be writable by CPU");
AZ_Assert((kDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ), "The resource should be readable by CPU");
bVideoMemory = false;
bAllocateSystemMemory = true;
break;
case D3D11_USAGE_TRANSIENT:
spBuffer->m_pfUnmapSubresource = &STransientBufferImpl::UnmapBuffer;
spBuffer->m_pfMapBufferRange = &STransientBufferImpl::MapBufferRange;
spBuffer->m_eUsage = eBU_MapInRingBufferTTLOnce;
// Igor: this buffer never own memory but rather borrows it from the ring buffer
bVideoMemory = false;
bAllocateSystemMemory = false;
if ((kDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ) != 0)
{
DXGL_ERROR("Cannot create a buffer with transient usage that is CPU readable");
return NULL;
}
break;
default:
case D3D11_USAGE_DIRECT_ACCESS:
spBuffer->m_pfUnmapSubresource = &SDirectAccessBufferImpl::UnmapBuffer;
spBuffer->m_pfMapBufferRange = &SDirectAccessBufferImpl::MapBufferRange;
spBuffer->m_pfMapSubresource = &SDirectAccessBufferImpl::MapBuffer;
spBuffer->m_eUsage = eBU_DirectAccess;
bVideoMemory = true;
bAllocateSystemMemory = false;
break;
DXGL_ERROR("Buffer usage not supported");
return NULL;
}
if (bVideoMemory)
{
// Confetti BEGIN: Igor Lobanchikov
id<MTLDevice> Device = pDevice->GetMetalDevice();
if (kDesc.BindFlags & D3D11_BIND_UNORDERED_ACCESS)
{
spBuffer->m_BufferShared = [Device newBufferWithLength:spBuffer->m_uSize options:MTLResourceStorageModeShared];
}
else
{
spBuffer->m_BufferShared = [Device newBufferWithLength:spBuffer->m_uSize options:MTLResourceCPUCacheModeWriteCombined | MTLResourceStorageModeShared];
}
if (pInitialData != NULL)
{
cryMemcpy(spBuffer->m_BufferShared.contents, pInitialData->pSysMem, kDesc.ByteWidth);
}
// Confetti End: Igor Lobanchikov
}
if (bAllocateSystemMemory)
{
spBuffer->m_pSystemMemoryCopy = static_cast<uint8*>(Memalign(kDesc.ByteWidth, MIN_MAPPED_RESOURCE_ALIGNMENT));
}
if (spBuffer->m_pSystemMemoryCopy != NULL && pInitialData != NULL)
{
cryMemcpy(spBuffer->m_pSystemMemoryCopy, pInitialData->pSysMem, kDesc.ByteWidth);
}
return spBuffer;
}
template <typename ViewDesc, typename View>
struct SResourceViewBaseImpl
{
typedef ViewDesc TViewDesc;
typedef View TView;
typedef _smart_ptr<TView> TViewPtr;
};
struct SShaderResourceViewImpl
: SResourceViewBaseImpl<D3D11_SHADER_RESOURCE_VIEW_DESC, SShaderResourceView>
{
enum EViewDimension
{
DIMENSION_BUFFER = D3D11_SRV_DIMENSION_BUFFER,
DIMENSION_TEXTURE1D = D3D11_SRV_DIMENSION_TEXTURE1D,
DIMENSION_TEXTURE1DARRAY = D3D11_SRV_DIMENSION_TEXTURE1DARRAY,
DIMENSION_TEXTURE2D = D3D11_SRV_DIMENSION_TEXTURE2D,
DIMENSION_TEXTURE2DARRAY = D3D11_SRV_DIMENSION_TEXTURE2DARRAY,
DIMENSION_TEXTURE2DMS = D3D11_SRV_DIMENSION_TEXTURE2DMS,
DIMENSION_TEXTURE2DMSARRAY = D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY,
DIMENSION_TEXTURE3D = D3D11_SRV_DIMENSION_TEXTURE3D,
DIMENSION_TEXTURECUBE = D3D11_SRV_DIMENSION_TEXTURECUBE,
DIMENSION_TEXTURECUBEARRAY = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY,
DIMENSION_BUFFEREX = D3D11_SRV_DIMENSION_BUFFEREX
};
static SResourceViewBaseImpl::TViewPtr GetView(STexture* pTexture, DXGI_FORMAT eDXGIFormat, MTLTextureType eViewType, uint32 uMinLevel, uint32 uNumLevels, uint32 uMinElement, uint32 uNumElements, CDevice* pDevice)
{
SShaderTextureViewConfiguration kConfiguration(GetGIFormat(eDXGIFormat), eViewType, uMinLevel, uNumLevels, uMinElement, uNumElements);
SShaderTextureView* pExistingView(pTexture->m_pShaderViewsHead);
while (pExistingView != NULL)
{
if (pExistingView->m_kConfiguration == kConfiguration)
{
return pExistingView;
}
pExistingView = pExistingView->m_pNextView;
}
return pTexture->CreateShaderView(kConfiguration, pDevice);
}
static SResourceViewBaseImpl::TViewPtr GetBufferView(SBuffer* pBuffer, DXGI_FORMAT eDXGIFormat, MTLTextureType eViewType, uint32 uMinLevel, uint32 uNumLevels, uint32 uMinElement, uint32 uNumElements, CDevice* pDevice)
{
SShaderBufferViewConfiguration kConfiguration(GetGIFormat(eDXGIFormat), eViewType, uMinLevel, uNumLevels, uMinElement, uNumElements);
return pBuffer->CreateShaderView(kConfiguration, pDevice);
}
template <typename DimDesc>
static TViewPtr GetViewMip(STexture* pTexture, const DimDesc& kDimDesc, DXGI_FORMAT eDXGIFormat, MTLTextureType eViewType, uint32 uMinElement, uint32 uNumElements, CDevice* pDevice)
{
uint32 uNumMipLevels(kDimDesc.MipLevels == -1 ? (pTexture->m_uNumMipLevels - kDimDesc.MostDetailedMip) : kDimDesc.MipLevels);
return GetView(pTexture, eDXGIFormat, eViewType, kDimDesc.MostDetailedMip, uNumMipLevels, uMinElement, uNumElements, pDevice);
}
template <typename DimDesc>
static TViewPtr GetViewLayers(STexture* pTexture, const DimDesc& kDimDesc, DXGI_FORMAT eDXGIFormat, uint32 uMinLevel, uint32 uNumLevels, MTLTextureType eViewType, CDevice* pDevice)
{
return GetView(pTexture, eDXGIFormat, eViewType, uMinLevel, uNumLevels, kDimDesc.FirstArraySlice, kDimDesc.ArraySize, pDevice);
}
template <typename DimDesc>
static TViewPtr GetViewMipLayers(STexture* pTexture, const DimDesc& kDimDesc, DXGI_FORMAT eDXGIFormat, MTLTextureType eViewType, CDevice* pDevice)
{
return GetViewMip(pTexture, kDimDesc, eDXGIFormat, eViewType, kDimDesc.FirstArraySlice, kDimDesc.ArraySize, pDevice);
}
};
template <typename ViewDesc>
struct SOutputMergerViewImpl
: SResourceViewBaseImpl<ViewDesc, SOutputMergerView>
{
static typename SOutputMergerViewImpl::TViewPtr GetView(STexture* pTexture, DXGI_FORMAT eDXGIFormat, MTLTextureType eViewType, uint32 uMipLevel, uint32, uint32 uMinElement, uint32 uNumElements, CDevice* pDevice)
{
return pTexture->GetCompatibleOutputMergerView(SOutputMergerTextureViewConfiguration(GetGIFormat(eDXGIFormat), uMipLevel, uMinElement, uNumElements), pDevice);
}
static typename SOutputMergerViewImpl::TViewPtr GetBufferView(SBuffer* pBuffer, DXGI_FORMAT eDXGIFormat, MTLTextureType eViewType, uint32 uMinLevel, uint32 uNumLevels, uint32 uMinElement, uint32 uNumElements, CDevice* pDevice)
{
//not implemented
CRY_ASSERT(0);
return NULL;
}
template <typename DimDesc>
static typename SOutputMergerViewImpl::TViewPtr GetViewMip(STexture* pTexture, const DimDesc& kDimDesc, DXGI_FORMAT eDXGIFormat, MTLTextureType eViewType, uint32 uMinElement, uint32 uNumElements, CDevice* pDevice)
{
return GetView(pTexture, eDXGIFormat, eViewType, kDimDesc.MipSlice, 1, uMinElement, uNumElements, pDevice);
}
template <typename DimDesc>
static typename SOutputMergerViewImpl::TViewPtr GetViewLayers(STexture* pTexture, const DimDesc& kDimDesc, DXGI_FORMAT eDXGIFormat, MTLTextureType eViewType, uint32 uMinLevel, uint32 uNumLevels, CDevice* pDevice)
{
return GetView(pTexture, eDXGIFormat, eViewType, uMinLevel, uNumLevels, kDimDesc.FirstArraySlice, kDimDesc.ArraySize, pDevice);
}
template <typename DimDesc>
static typename SOutputMergerViewImpl::TViewPtr GetViewMipLayers(STexture* pTexture, const DimDesc& kDimDesc, DXGI_FORMAT eDXGIFormat, MTLTextureType eViewType, CDevice* pDevice)
{
return GetViewMip(pTexture, kDimDesc, eDXGIFormat, eViewType, kDimDesc.FirstArraySlice, kDimDesc.ArraySize, pDevice);
}
};
struct SRenderTargetViewImpl
: SOutputMergerViewImpl<D3D11_RENDER_TARGET_VIEW_DESC>
{
enum EViewDimension
{
DIMENSION_BUFFER = D3D11_RTV_DIMENSION_BUFFER,
DIMENSION_TEXTURE1D = D3D11_RTV_DIMENSION_TEXTURE1D,
DIMENSION_TEXTURE1DARRAY = D3D11_RTV_DIMENSION_TEXTURE1DARRAY,
DIMENSION_TEXTURE2D = D3D11_RTV_DIMENSION_TEXTURE2D,
DIMENSION_TEXTURE2DARRAY = D3D11_RTV_DIMENSION_TEXTURE2DARRAY,
DIMENSION_TEXTURE2DMS = D3D11_RTV_DIMENSION_TEXTURE2DMS,
DIMENSION_TEXTURE2DMSARRAY = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY,
DIMENSION_TEXTURE3D = D3D11_RTV_DIMENSION_TEXTURE3D
};
};
struct SDepthStencilViewImpl
: SOutputMergerViewImpl<D3D11_DEPTH_STENCIL_VIEW_DESC>
{
enum EViewDimension
{
DIMENSION_TEXTURE1D = D3D11_DSV_DIMENSION_TEXTURE1D,
DIMENSION_TEXTURE1DARRAY = D3D11_DSV_DIMENSION_TEXTURE1DARRAY,
DIMENSION_TEXTURE2D = D3D11_DSV_DIMENSION_TEXTURE2D,
DIMENSION_TEXTURE2DARRAY = D3D11_DSV_DIMENSION_TEXTURE2DARRAY,
DIMENSION_TEXTURE2DMS = D3D11_DSV_DIMENSION_TEXTURE2DMS,
DIMENSION_TEXTURE2DMSARRAY = D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY,
};
};
template <typename Impl>
typename Impl::TViewPtr GetTexture1DView(STexture* pTexture, const typename Impl::TViewDesc& kViewDesc, CDevice* pDevice)
{
using ViewDimensionType = decltype(kViewDesc.ViewDimension);
switch (kViewDesc.ViewDimension)
{
case static_cast<ViewDimensionType>(Impl::DIMENSION_TEXTURE1D):
return Impl::GetViewMip(pTexture, kViewDesc.Texture1D, kViewDesc.Format, MTLTextureType1D, 0, 1, pDevice);
case static_cast<ViewDimensionType>(Impl::DIMENSION_TEXTURE1DARRAY):
return Impl::GetViewMipLayers(pTexture, kViewDesc.Texture1DArray, kViewDesc.Format, MTLTextureType1DArray, pDevice);
}
return NULL;
}
template <typename Impl>
typename Impl::TViewPtr GetTexture2DView(STexture* pTexture, const typename Impl::TViewDesc& kViewDesc, CDevice* pDevice)
{
using ViewDimensionType = decltype(kViewDesc.ViewDimension);
switch (kViewDesc.ViewDimension)
{
case static_cast<ViewDimensionType>(Impl::DIMENSION_TEXTURE2D):
return Impl::GetViewMip(pTexture, kViewDesc.Texture2D, kViewDesc.Format, MTLTextureType2D, 0, 1, pDevice);
case static_cast<ViewDimensionType>(Impl::DIMENSION_TEXTURE2DARRAY):
return Impl::GetViewMipLayers(pTexture, kViewDesc.Texture2DArray, kViewDesc.Format, MTLTextureType2DArray, pDevice);
#if DXGL_SUPPORT_MULTISAMPLED_TEXTURES
case static_cast<ViewDimensionType>(Impl::DIMENSION_TEXTURE2DMS):
return Impl::GetView(pTexture, kViewDesc.Format, GL_TEXTURE_2D_MULTISAMPLE, 0, 1, 0, 1, pDevice);
#endif //DXGL_SUPPORT_MULTISAMPLED_TEXTURES
}
return NULL;
}
template <typename Impl>
typename Impl::TViewPtr GetTextureCubeView(STexture* pTexture, const typename Impl::TViewDesc& kViewDesc, CDevice* pDevice)
{
using ViewDimensionType = decltype(kViewDesc.ViewDimension);
switch (kViewDesc.ViewDimension)
{
case static_cast<ViewDimensionType>(D3D11_SRV_DIMENSION_TEXTURECUBE):
DXGL_TODO("Check if 6 is correct");
return Impl::GetViewMip(pTexture, kViewDesc.TextureCube, kViewDesc.Format, MTLTextureTypeCube, 0, 6, pDevice);
}
return NULL;
}
template <typename Impl>
typename Impl::TViewPtr GetTexture3DView(STexture* pTexture, const typename Impl::TViewDesc& kViewDesc, CDevice* pDevice)
{
if ((uint32)kViewDesc.ViewDimension == (uint32)Impl::DIMENSION_TEXTURE3D)
{
return Impl::GetViewMip(pTexture, kViewDesc.Texture3D, kViewDesc.Format, MTLTextureType3D, 0, 1, pDevice);
}
return NULL;
}
SShaderResourceViewImpl a;
// Confetti BEGIN: Igor Lobanchikov
template <>
SRenderTargetViewImpl::TViewPtr GetTexture3DView<SRenderTargetViewImpl>(STexture* pTexture, const SRenderTargetViewImpl::TViewDesc& kViewDesc, CDevice* pDevice)
{
// DX11 does not support the array 3D texture rendering so route 3D slice down as the arraiy slice
// iOS does not support rendering into the multiple slices at the same time, so the check is performed later in
// the pipeline.
// Igor: double check the following code when we meet this firts. This wasn't used hence wasn't tested.
DXGL_NOT_IMPLEMENTED;
// Confetti End: Igor Lobanchikov
if ((uint32)kViewDesc.ViewDimension == (uint32)SRenderTargetViewImpl::DIMENSION_TEXTURE3D)
{
return SRenderTargetViewImpl::GetViewMip(pTexture, kViewDesc.Texture3D, kViewDesc.Format, MTLTextureType3D,
kViewDesc.Texture3D.FirstWSlice, kViewDesc.Texture3D.WSize, pDevice);
}
return NULL;
}
// Confetti End: Igor Lobanchikov
template <typename Impl>
typename Impl::TViewPtr GetBufferView(SBuffer* pBuffer, const typename Impl::TViewDesc& kViewDesc, CDevice* pDevice)
{
return Impl::GetBufferView(pBuffer, kViewDesc.Format, MTLTextureType1D, 1, 1, 0, 1, pDevice);
}
template <typename Impl>
typename Impl::TViewPtr GetBufferViewEx(SBuffer* pBuffer, const typename Impl::TViewDesc& kViewDesc, CDevice* pDevice)
{
DXMETAL_NOT_IMPLEMENTED
CryLog("TODO: GetBufferViewEX");
return NULL;
}
SShaderResourceViewPtr CreateShaderResourceView(SResource* pResource, D3D11_RESOURCE_DIMENSION eDimension, const D3D11_SHADER_RESOURCE_VIEW_DESC& kViewDesc, CDevice* pDevice /*CContext* pContext*/)
{
DXGL_SCOPED_PROFILE("CreateShaderResourceView")
typedef SShaderResourceViewImpl TImpl;
SShaderResourceViewPtr spView;
switch (eDimension)
{
case D3D11_RESOURCE_DIMENSION_TEXTURE1D:
spView = GetTexture1DView<TImpl>(static_cast<STexture*>(pResource), kViewDesc, pDevice);
break;
case D3D11_RESOURCE_DIMENSION_TEXTURE2D:
spView = GetTexture2DView<TImpl>(static_cast<STexture*>(pResource), kViewDesc, pDevice);
if (spView == NULL)
{
spView = GetTextureCubeView<TImpl>(static_cast<STexture*>(pResource), kViewDesc, pDevice);
}
break;
case D3D11_RESOURCE_DIMENSION_TEXTURE3D:
spView = GetTexture3DView<TImpl>(static_cast<STexture*>(pResource), kViewDesc, pDevice);
break;
case D3D11_RESOURCE_DIMENSION_BUFFER:
spView = GetBufferView<TImpl>(static_cast<SBuffer*>(pResource), kViewDesc, pDevice);
if (spView == NULL)
{
spView = GetBufferViewEx<TImpl>(static_cast<SBuffer*>(pResource), kViewDesc, pDevice);
}
break;
default:
DXGL_ERROR("Invalid resource dimension for shader resource");
return NULL;
}
if (spView == NULL)
{
DXGL_ERROR("Invalid shader resource view paramters");
}
return spView;
}
SOutputMergerViewPtr CreateRenderTargetView(SResource* pResource, D3D11_RESOURCE_DIMENSION eDimension, const D3D11_RENDER_TARGET_VIEW_DESC& kViewDesc, CDevice* pDevice /*CContext* pContext*/)
{
DXGL_SCOPED_PROFILE("CreateRenderTargetView")
typedef SRenderTargetViewImpl TImpl;
switch (eDimension)
{
case D3D11_RESOURCE_DIMENSION_TEXTURE1D:
return GetTexture1DView<TImpl>(static_cast<STexture*>(pResource), kViewDesc, pDevice);
case D3D11_RESOURCE_DIMENSION_TEXTURE2D:
return GetTexture2DView<TImpl>(static_cast<STexture*>(pResource), kViewDesc, pDevice);
case D3D11_RESOURCE_DIMENSION_TEXTURE3D:
return GetTexture3DView<TImpl>(static_cast<STexture*>(pResource), kViewDesc, pDevice);
case D3D11_RESOURCE_DIMENSION_BUFFER:
return GetBufferView<TImpl>(static_cast<SBuffer*>(pResource), kViewDesc, pDevice);
}
DXGL_ERROR("Invalid resource dimension for render target");
return NULL;
}
SOutputMergerViewPtr CreateDepthStencilView(SResource* pResource, D3D11_RESOURCE_DIMENSION eDimension, const D3D11_DEPTH_STENCIL_VIEW_DESC& kViewDesc, CDevice* pDevice /*CContext* pContext*/)
{
DXGL_SCOPED_PROFILE("CreateDepthStencilView")
typedef SDepthStencilViewImpl TImpl;
switch (eDimension)
{
case D3D11_RESOURCE_DIMENSION_TEXTURE1D:
return GetTexture1DView<TImpl>(static_cast<STexture*>(pResource), kViewDesc, pDevice);
case D3D11_RESOURCE_DIMENSION_TEXTURE2D:
return GetTexture2DView<TImpl>(static_cast<STexture*>(pResource), kViewDesc, pDevice);
}
DXGL_ERROR("Invalid resource dimension for render target");
return NULL;
}
SQueryPtr CreateQuery(const D3D11_QUERY_DESC& kDesc, CDevice* pDevice /*CContext* pContext*/)
{
DXGL_SCOPED_PROFILE("CreateQuery")
SQueryPtr spQuery(NULL);
switch (kDesc.Query)
{
case D3D11_QUERY_OCCLUSION:
{
SOcclusionQuery* pOcclusionQuery(new SOcclusionQuery());
spQuery = pOcclusionQuery;
}
break;
case D3D11_QUERY_EVENT:
spQuery = new SFenceSync();
break;
case D3D11_QUERY_TIMESTAMP:
case D3D11_QUERY_TIMESTAMP_DISJOINT:
case D3D11_QUERY_PIPELINE_STATISTICS:
case D3D11_QUERY_OCCLUSION_PREDICATE:
case D3D11_QUERY_SO_STATISTICS:
case D3D11_QUERY_SO_OVERFLOW_PREDICATE:
case D3D11_QUERY_SO_STATISTICS_STREAM0:
case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM0:
case D3D11_QUERY_SO_STATISTICS_STREAM1:
case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM1:
case D3D11_QUERY_SO_STATISTICS_STREAM2:
case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM2:
case D3D11_QUERY_SO_STATISTICS_STREAM3:
case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM3:
DXGL_NOT_IMPLEMENTED
break;
}
return spQuery;
}
SDefaultFrameBufferTexturePtr CreateBackBufferTexture(const D3D11_TEXTURE2D_DESC& kDesc)
{
DXGL_SCOPED_PROFILE("CreateBackBufferTexture")
EGIFormat eGIFormat(GetGIFormat(kDesc.Format));
if (eGIFormat == eGIF_NUM)
{
return NULL;
}
return new SDefaultFrameBufferTexture(kDesc.Width, kDesc.Height, eGIFormat);
}
typedef void (* CopyTextureBoxFunc)(STexture*, STexPos, STexSubresourceID, STexture*, STexPos, STexSubresourceID, STexSize, CContext*);
void CopySystemTextureBox(
STexture* pDstTexture, STexPos kDstPos, STexSubresourceID kDstSubID,
STexture* pSrcTexture, STexPos kSrcPos, STexSubresourceID kSrcSubID,
STexSize kBoxSize, CContext* pContext)
{
UINT uDstSubresource(D3D11CalcSubresource(kDstSubID.m_iMipLevel, kDstSubID.m_uElement, pDstTexture->m_uNumMipLevels));
UINT uSrcSubresource(D3D11CalcSubresource(kSrcSubID.m_iMipLevel, kSrcSubID.m_uElement, pSrcTexture->m_uNumMipLevels));
D3D11_BOX kDstBox;
kDstBox.left = kDstPos.x;
kDstBox.top = kDstPos.y;
kDstBox.front = kDstPos.z;
kDstBox.right = kDstPos.x + kBoxSize.x;
kDstBox.bottom = kDstPos.y + kBoxSize.y;
kDstBox.back = kDstPos.z + kBoxSize.z;
D3D11_MAPPED_SUBRESOURCE kSrcMapped;
pSrcTexture->m_pfMapSubresource(pSrcTexture, uSrcSubresource, D3D11_MAP_READ, 0, &kSrcMapped, pContext);
pDstTexture->m_pfUpdateSubresource(pDstTexture, uDstSubresource, &kDstBox, kSrcMapped.pData, kSrcMapped.RowPitch, kSrcMapped.DepthPitch, pContext);
pSrcTexture->m_pfUnmapSubresource(pSrcTexture, uSrcSubresource, pContext);
}
void CopyTextureWithBlitCommandEncoder(
STexture* pDstTexture, STexPos kDstPos, STexSubresourceID kDstSubID,
STexture* pSrcTexture, STexPos kSrcPos, STexSubresourceID kSrcSubID,
STexSize kBoxSize, CContext* pContext)
{
MTLOrigin sourceOrigin = MTLOriginMake(kSrcPos.x, kSrcPos.y, kSrcPos.z);
MTLSize sourceSize = MTLSizeMake(kBoxSize.x, kBoxSize.y, kBoxSize.z);
MTLOrigin destinationOrigin = {static_cast<NSUInteger>(kDstPos.x),
static_cast<NSUInteger>(kDstPos.y),
static_cast<NSUInteger>(kDstPos.z)};
id<MTLBlitCommandEncoder> blitCommandEncoder = pContext->GetBlitCommandEncoder();
[blitCommandEncoder copyFromTexture: pSrcTexture->m_Texture
sourceSlice: kSrcSubID.m_uElement
sourceLevel: kSrcSubID.m_iMipLevel
sourceOrigin: sourceOrigin
sourceSize: sourceSize
toTexture: pDstTexture->m_Texture
destinationSlice: kDstSubID.m_uElement
destinationLevel: kDstSubID.m_iMipLevel
destinationOrigin: destinationOrigin];
#if defined AZ_PLATFORM_MAC
if (pDstTexture->m_Texture.storageMode == MTLStorageModeManaged)
{
// Need to synchronize the CPU/GPU versions of the texture if it is
// in manged storage mode otherwise the CPU may not see any of the
// writes the GPU does to the texture
[blitCommandEncoder synchronizeTexture: pDstTexture->m_Texture
slice: kDstSubID.m_uElement
level: kDstSubID.m_iMipLevel];
}
#endif
}
template <CopyTextureBoxFunc pfCopyTextureBox>
void CopyTextureImpl(STexture* pDstTexture, STexture* pSrcTexture, CContext* pContext)
{
DXGL_TODO("Check if there's a better way to do this");
STexSubresourceID kSubID;
for (kSubID.m_iMipLevel = 0; kSubID.m_iMipLevel < (GLint)pDstTexture->m_uNumMipLevels; ++kSubID.m_iMipLevel)
{
STexBox kBox;
GetTextureBox(kBox, pDstTexture, kSubID.m_iMipLevel, GetGIFormatInfo(pDstTexture->m_eFormat), true);
for (kSubID.m_uElement = 0; kSubID.m_uElement < pDstTexture->m_uNumElements; ++kSubID.m_uElement)
{
pfCopyTextureBox(
pDstTexture, kBox.m_kOffset, kSubID,
pSrcTexture, kBox.m_kOffset, kSubID,
kBox.m_kSize, pContext);
}
}
}
void CopyTexture(STexture* pDstTexture, STexture* pSrcTexture, CContext* pContext)
{
DXGL_SCOPED_PROFILE("CopyTexture")
if (pSrcTexture->m_uNumMipLevels != pDstTexture->m_uNumMipLevels ||
pSrcTexture->m_uNumElements != pDstTexture->m_uNumElements ||
pSrcTexture->m_iWidth != pDstTexture->m_iWidth ||
pSrcTexture->m_iHeight != pDstTexture->m_iHeight ||
pSrcTexture->m_iDepth != pDstTexture->m_iDepth)
{
DXGL_ERROR("Source and destination textures to copy don't match");
return;
}
// Confetti BEGIN: Igor Lobanchikov
CopyTextureImpl<CopyTextureWithBlitCommandEncoder>(pDstTexture, pSrcTexture, pContext);
// Confetti End: Igor Lobanchikov
}
template <CopyTextureBoxFunc pfCopyTextureBox>
void CopySubTextureImpl(
STexture* pDstTexture, uint32 uDstSubresource, uint32 uDstX, uint32 uDstY, uint32 uDstZ,
STexture* pSrcTexture, uint32 uSrcSubresource, const D3D11_BOX* pSrcBox, CContext* pContext)
{
STexSubresourceID kDstSubID;
kDstSubID.m_iMipLevel = uDstSubresource % pDstTexture->m_uNumMipLevels;
kDstSubID.m_uElement = uDstSubresource / pDstTexture->m_uNumMipLevels;
STexSubresourceID kSrcSubID;
kSrcSubID.m_iMipLevel = uSrcSubresource % pSrcTexture->m_uNumMipLevels;
kSrcSubID.m_uElement = uSrcSubresource / pSrcTexture->m_uNumMipLevels;
STexBox kSrcBox;
GetTextureBox(kSrcBox, pSrcTexture, kSrcSubID.m_iMipLevel, pSrcBox, GetGIFormatInfo(pSrcTexture->m_eFormat), true);
STexPos kDstPos(uDstX, uDstY, uDstZ);
pfCopyTextureBox(
pDstTexture, kDstPos, kDstSubID,
pSrcTexture, kSrcBox.m_kOffset, kSrcSubID,
kSrcBox.m_kSize, pContext);
}
void CopySubTexture(
STexture* pDstTexture, uint32 uDstSubresource, uint32 uDstX, uint32 uDstY, uint32 uDstZ,
STexture* pSrcTexture, uint32 uSrcSubresource, const D3D11_BOX* pSrcBox, CContext* pContext)
{
DXGL_SCOPED_PROFILE("CopySubTexture")
// Confetti BEGIN: Igor Lobanchikov
assert(pDstTexture->m_Texture);
assert(pSrcTexture->m_Texture);
if (pDstTexture->m_eFormat != pSrcTexture->m_eFormat)
{
pContext->TrySlowCopySubresource(pDstTexture, uDstSubresource, uDstX, uDstY, uDstZ, pSrcTexture, uSrcSubresource, pSrcBox);
return;
}
CopySubTextureImpl<CopyTextureWithBlitCommandEncoder>(pDstTexture, uDstSubresource, uDstX, uDstY, uDstZ, pSrcTexture, uSrcSubresource, pSrcBox, pContext);
}
void CopySubBufferInternal(SBuffer* pDstBuffer, SBuffer* pSrcBuffer, uint32 uDstOffset, uint32 uSrcOffset, uint32 uSize, CContext* pContext)
{
if (pSrcBuffer->m_pSystemMemoryCopy != NULL && pDstBuffer->m_pSystemMemoryCopy != NULL)
{
cryMemcpy(pDstBuffer->m_pSystemMemoryCopy + uDstOffset, pSrcBuffer->m_pSystemMemoryCopy + uSrcOffset, uSize);
}
// Confetti BEGIN: Igor Lobanchikov
assert(pDstBuffer->m_BufferShared);
// assert(pSrcBuffer->m_Buffer);
size_t ringBufferOffsetOut = 0;
MemRingBufferStorage memAllocMode = GetMemAllocModeBasedOnSize(uSize);
id<MTLBuffer> tmpBuffer = pSrcBuffer->m_BufferShared;
if (!tmpBuffer)
{
assert(pSrcBuffer->m_pSystemMemoryCopy);
tmpBuffer = pContext->GetRingBuffer(memAllocMode);
void* pTempData = pContext->AllocateMemoryInRingBuffer(uSize, memAllocMode, ringBufferOffsetOut);
size_t tmpOffset = (uint8*)pTempData - (uint8*)tmpBuffer.contents;
cryMemcpy(pTempData, pSrcBuffer->m_pSystemMemoryCopy + uSrcOffset, uSize);
uSrcOffset = tmpOffset;
}
id<MTLBlitCommandEncoder> blitCommandEncoder = pContext->GetBlitCommandEncoder();
[blitCommandEncoder copyFromBuffer: tmpBuffer sourceOffset: uSrcOffset toBuffer: pDstBuffer->m_BufferShared destinationOffset: uDstOffset size: uSize];
// Confetti End: Igor Lobanchikov
}
void CopyBuffer(SBuffer* pDstBuffer, SBuffer* pSrcBuffer, CContext* pContext)
{
DXGL_SCOPED_PROFILE("CopyBuffer")
if (pSrcBuffer->m_uSize != pDstBuffer->m_uSize)
{
DXGL_ERROR("Source and destination buffers to copy don't match");
return;
}
CopySubBufferInternal(pDstBuffer, pSrcBuffer, 0, 0, pSrcBuffer->m_uSize, pContext);
}
void CopySubBuffer(
SBuffer* pDstBuffer, uint32 uDstSubresource, uint32 uDstX, uint32, uint32,
SBuffer* pSrcBuffer, uint32 uSrcSubresource, const D3D11_BOX* pSrcBox, CContext* pContext)
{
DXGL_SCOPED_PROFILE("CopySubBuffer")
uint32 uSrcBegin, uSrcEnd;
if (pSrcBox != NULL)
{
uSrcBegin = pSrcBox->left;
uSrcEnd = pSrcBox->right;
}
else
{
uSrcBegin = 0;
uSrcEnd = pSrcBuffer->m_uSize;
}
uint32 uSize(uSrcEnd - uSrcBegin);
if (uSrcBegin > pSrcBuffer->m_uSize ||
uSrcEnd > pSrcBuffer->m_uSize ||
uDstX + uSize > pDstBuffer->m_uSize)
{
DXGL_ERROR("Source or destination range out of bounds");
return;
}
if (pDstBuffer != pSrcBuffer)
{
CopySubBufferInternal(pDstBuffer, pSrcBuffer, uDstX, uSrcBegin, uSize, pContext);
}
else
{
// Igor: check if this works as expected once triggered.
assert(!"Not tested");
// Igor: don't want to copy
if (uDstX == uSrcBegin)
{
return;
}
// copy forward
else if (uSrcBegin > uDstX && uSrcBegin < uDstX + uSize)
{
uint32 chunkSize = uSrcBegin - uDstX;
while (uSize)
{
chunkSize = min(chunkSize, uSize);
CopySubBufferInternal(pDstBuffer, pSrcBuffer, uDstX, uSrcBegin, chunkSize, pContext);
pDstBuffer += chunkSize;
pSrcBuffer += chunkSize;
uSize -= chunkSize;
}
}
// copy backward
else if (uDstX > uSrcBegin && uDstX < uSrcBegin + uSize)
{
uint32 chunkSize = uDstX - uSrcBegin;
uDstX = uSrcBegin + uSize;
uSrcBegin = uDstX - chunkSize;
while (uSize)
{
chunkSize = min(chunkSize, uSize);
CopySubBufferInternal(pDstBuffer, pSrcBuffer, uDstX, uSrcBegin, chunkSize, pContext);
pDstBuffer -= chunkSize;
pSrcBuffer -= chunkSize;
uSize -= chunkSize;
}
}
// default behaviour
else
{
CopySubBufferInternal(pDstBuffer, pSrcBuffer, uDstX, uSrcBegin, uSize, pContext);
}
}
}
}