Metal: support OES_depth_texture

Also added Depth32 & Depth16 texture data upload tests.

Bug: angleproject:2634
Change-Id: I103f1cda1dc915f0dc8b04f7aaa2d8c0f9220cda
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1919281
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
This commit is contained in:
Le Hoang Quyen
2019-11-19 23:04:00 +08:00
committed by Commit Bot
parent 7587588595
commit d1860ea17e
19 changed files with 598 additions and 59 deletions

View File

@@ -20,6 +20,12 @@ struct FeaturesMtl : FeatureSetBase
Feature hasBaseVertexInstancedDraw = {
"has_base_vertex_instanced_draw", FeatureCategory::MetalFeatures,
"The renderer supports base vertex instanced draw", &members};
// Support depth texture filtering
Feature hasDepthTextureFiltering = {
"has_depth_texture_filtering", FeatureCategory::MetalFeatures,
"The renderer supports depth texture's filtering other than nearest", &members};
// Non-uniform compute shader dispatch support, i.e. Group size is not necessarily to be fixed:
Feature hasNonUniformDispatch = {
"has_non_uniform_dispatch", FeatureCategory::MetalFeatures,

View File

@@ -4,7 +4,7 @@
"src/libANGLE/renderer/metal/gen_mtl_format_table.py":
"b6468446dd1da3e44ac9dd11690b5bf1",
"src/libANGLE/renderer/metal/mtl_format_map.json":
"c6f5b6dda11e456cfbcaeec53eb46fa0",
"0166b70697732612bd46942e7ccf8e6e",
"src/libANGLE/renderer/metal/mtl_format_table_autogen.mm":
"706e07c2bf5b50fa031678a5c2372465"
"8796be69461e2bb07db3b81b6ef24432"
}

View File

@@ -1799,7 +1799,7 @@ void R11G11B10F::average(R11G11B10F *dst, const R11G11B10F *src1, const R11G11B1
void D24S8::ReadDepthStencil(DepthStencil *dst, const D24S8 *src)
{
dst->depth = gl::normalizedToFloat<24>(src->D);
dst->stencil = gl::getShiftedData<8, 24>(src->S);
dst->stencil = src->S;
}
void D24S8::WriteDepthStencil(D24S8 *dst, const DepthStencil *src)
@@ -1810,22 +1810,24 @@ void D24S8::WriteDepthStencil(D24S8 *dst, const DepthStencil *src)
void S8::ReadDepthStencil(DepthStencil *dst, const S8 *src)
{
UNIMPLEMENTED();
dst->depth = 0;
dst->stencil = src->S;
}
void S8::WriteDepthStencil(S8 *dst, const DepthStencil *src)
{
UNIMPLEMENTED();
dst->S = src->stencil & 0xFF;
}
void D16::ReadDepthStencil(DepthStencil *dst, const D16 *src)
{
UNIMPLEMENTED();
dst->depth = gl::normalizedToFloat(src->D);
dst->stencil = 0;
}
void D16::WriteDepthStencil(D16 *dst, const DepthStencil *src)
{
UNIMPLEMENTED();
dst->D = gl::floatToNormalized<uint16_t>(static_cast<float>(src->depth));
}
void D24X8::ReadDepthStencil(DepthStencil *dst, const D24X8 *src)
@@ -1835,7 +1837,7 @@ void D24X8::ReadDepthStencil(DepthStencil *dst, const D24X8 *src)
void D24X8::WriteDepthStencil(D24X8 *dst, const DepthStencil *src)
{
UNIMPLEMENTED();
dst->D = gl::floatToNormalized<24, uint32_t>(static_cast<float>(src->depth));
}
void D32F::ReadDepthStencil(DepthStencil *dst, const D32F *src)
@@ -1845,23 +1847,24 @@ void D32F::ReadDepthStencil(DepthStencil *dst, const D32F *src)
void D32F::WriteDepthStencil(D32F *dst, const DepthStencil *src)
{
UNIMPLEMENTED();
dst->D = static_cast<float>(src->depth);
}
void D32::ReadDepthStencil(DepthStencil *dst, const D32 *src)
{
UNIMPLEMENTED();
dst->depth = gl::normalizedToFloat(src->D);
dst->stencil = 0;
}
void D32::WriteDepthStencil(D32 *dst, const DepthStencil *src)
{
UNIMPLEMENTED();
dst->D = gl::floatToNormalized<uint32_t>(static_cast<float>(src->depth));
}
void D32FS8X24::ReadDepthStencil(DepthStencil *dst, const D32FS8X24 *src)
{
dst->depth = src->D;
dst->stencil = gl::getShiftedData<8, 24>(static_cast<uint32_t>(src->S));
dst->stencil = src->S;
}
void D32FS8X24::WriteDepthStencil(D32FS8X24 *dst, const DepthStencil *src)

View File

@@ -726,8 +726,8 @@ static_assert(sizeof(R11G11B10F) == 4, "R11G11B10F struct not 32-bits.");
struct D24S8
{
uint32_t D : 24;
uint32_t S : 8;
uint32_t D : 24;
static void ReadDepthStencil(DepthStencil *dst, const D24S8 *src);
static void WriteDepthStencil(D24S8 *dst, const DepthStencil *src);

View File

@@ -347,7 +347,7 @@ class Framebuffer final : public angle::ObserverInterface,
DIRTY_BIT_COLOR_BUFFER_CONTENTS_0,
DIRTY_BIT_COLOR_BUFFER_CONTENTS_MAX =
DIRTY_BIT_COLOR_BUFFER_CONTENTS_0 + IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS,
DIRTY_BIT_DEPTH_BUFFER_CONTENTS,
DIRTY_BIT_DEPTH_BUFFER_CONTENTS = DIRTY_BIT_COLOR_BUFFER_CONTENTS_MAX,
DIRTY_BIT_STENCIL_BUFFER_CONTENTS,
DIRTY_BIT_DRAW_BUFFERS,
DIRTY_BIT_READ_BUFFER,

View File

@@ -614,11 +614,15 @@ void DisplayMtl::initializeFeatures()
{
// default values:
mFeatures.hasBaseVertexInstancedDraw.enabled = true;
mFeatures.hasDepthTextureFiltering.enabled = false;
mFeatures.hasNonUniformDispatch.enabled = true;
mFeatures.hasTextureSwizzle.enabled = false;
mFeatures.allowSeparatedDepthStencilBuffers.enabled = false;
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
mFeatures.hasDepthTextureFiltering.enabled = true;
// Texture swizzle is only supported if macos sdk 10.15 is present
# if defined(__MAC_10_15)
if (ANGLE_APPLE_AVAILABLE_XC(10.15, 13.0))

View File

@@ -161,6 +161,7 @@ class TextureMtl : public TextureImpl
private:
void releaseTexture(bool releaseImages);
angle::Result ensureSamplerStateCreated(const gl::Context *context);
// Ensure image at given index is created:
angle::Result ensureImageCreated(const gl::Context *context, const gl::ImageIndex &index);
angle::Result checkForEmulatedChannels(const gl::Context *context,

View File

@@ -10,6 +10,7 @@
#include "libANGLE/renderer/metal/TextureMtl.h"
#include "common/Color.h"
#include "common/MemoryBuffer.h"
#include "common/debug.h"
#include "common/mathutil.h"
@@ -114,6 +115,207 @@ gl::TextureType GetTextureImageType(gl::TextureType texType)
}
}
angle::Result CopyTextureContentsToStagingBuffer(ContextMtl *contextMtl,
const angle::Format &textureAngleFormat,
const angle::Format &stagingAngleFormat,
const MTLSize &regionSize,
const uint8_t *data,
size_t bytesPerRow,
size_t *bufferRowPitchOut,
size_t *buffer2DImageSizeOut,
mtl::BufferRef *bufferOut)
{
// NOTE(hqle): 3D textures not supported yet.
ASSERT(regionSize.depth == 1);
size_t stagingBufferRowPitch = regionSize.width * stagingAngleFormat.pixelBytes;
size_t stagingBuffer2DImageSize = stagingBufferRowPitch * regionSize.height;
size_t stagingBufferSize = stagingBuffer2DImageSize * regionSize.depth;
mtl::BufferRef stagingBuffer;
ANGLE_TRY(mtl::Buffer::MakeBuffer(contextMtl, stagingBufferSize, nullptr, &stagingBuffer));
uint8_t *pdst = stagingBuffer->map(contextMtl);
if (textureAngleFormat.id == stagingAngleFormat.id)
{
for (NSUInteger r = 0; r < regionSize.height; ++r)
{
const uint8_t *pCopySrc = data + r * bytesPerRow;
uint8_t *pCopyDst = pdst + r * stagingBufferRowPitch;
memcpy(pCopyDst, pCopySrc, stagingBufferRowPitch);
}
}
else
{
// This is only for depth & stencil case.
ASSERT(textureAngleFormat.depthBits || textureAngleFormat.stencilBits);
ASSERT(textureAngleFormat.pixelReadFunction && stagingAngleFormat.pixelWriteFunction);
// cache to store read result of source pixel
angle::DepthStencil depthStencilData;
auto sourcePixelReadData = reinterpret_cast<uint8_t *>(&depthStencilData);
ASSERT(textureAngleFormat.pixelBytes <= sizeof(depthStencilData));
for (NSUInteger r = 0; r < regionSize.height; ++r)
{
for (NSUInteger c = 0; c < regionSize.width; ++c)
{
const uint8_t *sourcePixelData =
data + r * bytesPerRow + c * textureAngleFormat.pixelBytes;
uint8_t *destPixelData =
pdst + r * stagingBufferRowPitch + c * stagingAngleFormat.pixelBytes;
textureAngleFormat.pixelReadFunction(sourcePixelData, sourcePixelReadData);
stagingAngleFormat.pixelWriteFunction(sourcePixelReadData, destPixelData);
}
}
}
stagingBuffer->unmap(contextMtl);
*bufferOut = stagingBuffer;
*bufferRowPitchOut = stagingBufferRowPitch;
*buffer2DImageSizeOut = stagingBuffer2DImageSize;
return angle::Result::Continue;
}
angle::Result UploadTextureContentsWithStagingBuffer(ContextMtl *contextMtl,
const mtl::TextureRef &texture,
const angle::Format &textureAngleFormat,
MTLRegion region,
uint32_t mipmapLevel,
uint32_t slice,
const uint8_t *data,
size_t bytesPerRow)
{
ASSERT(texture && texture->valid());
ASSERT(!texture->isCPUAccessible());
ASSERT(!textureAngleFormat.depthBits || !textureAngleFormat.stencilBits);
// Compressed texture is not supporte atm
ASSERT(!textureAngleFormat.isBlock);
// Copy data to staging buffer
size_t stagingBufferRowPitch;
size_t stagingBuffer2DImageSize;
mtl::BufferRef stagingBuffer;
ANGLE_TRY(CopyTextureContentsToStagingBuffer(
contextMtl, textureAngleFormat, textureAngleFormat, region.size, data, bytesPerRow,
&stagingBufferRowPitch, &stagingBuffer2DImageSize, &stagingBuffer));
// Copy staging buffer to texture.
mtl::BlitCommandEncoder *encoder = contextMtl->getBlitCommandEncoder();
encoder->copyBufferToTexture(stagingBuffer, 0, stagingBufferRowPitch, stagingBuffer2DImageSize,
region.size, texture, slice, mipmapLevel, region.origin,
MTLBlitOptionNone);
return angle::Result::Continue;
}
// Packed depth stencil upload using staging buffer
angle::Result UploadPackedDepthStencilTextureContentsWithStagingBuffer(
ContextMtl *contextMtl,
const mtl::TextureRef &texture,
const angle::Format &textureAngleFormat,
MTLRegion region,
uint32_t mipmapLevel,
uint32_t slice,
const uint8_t *data,
size_t bytesPerRow)
{
ASSERT(texture && texture->valid());
ASSERT(!texture->isCPUAccessible());
ASSERT(textureAngleFormat.depthBits && textureAngleFormat.stencilBits);
// We have to split the depth & stencil data into 2 buffers.
angle::FormatID stagingDepthBufferFormatId;
angle::FormatID stagingStencilBufferFormatId;
switch (textureAngleFormat.id)
{
case angle::FormatID::D24_UNORM_S8_UINT:
stagingDepthBufferFormatId = angle::FormatID::D24_UNORM_X8_UINT;
stagingStencilBufferFormatId = angle::FormatID::S8_UINT;
break;
case angle::FormatID::D32_FLOAT_S8X24_UINT:
stagingDepthBufferFormatId = angle::FormatID::D32_FLOAT;
stagingStencilBufferFormatId = angle::FormatID::S8_UINT;
break;
default:
ANGLE_MTL_UNREACHABLE(contextMtl);
}
const angle::Format &angleStagingDepthFormat = angle::Format::Get(stagingDepthBufferFormatId);
const angle::Format &angleStagingStencilFormat =
angle::Format::Get(stagingStencilBufferFormatId);
size_t stagingDepthBufferRowPitch, stagingStencilBufferRowPitch;
size_t stagingDepthBuffer2DImageSize, stagingStencilBuffer2DImageSize;
mtl::BufferRef stagingDepthbuffer, stagingStencilBuffer;
ANGLE_TRY(CopyTextureContentsToStagingBuffer(
contextMtl, textureAngleFormat, angleStagingDepthFormat, region.size, data, bytesPerRow,
&stagingDepthBufferRowPitch, &stagingDepthBuffer2DImageSize, &stagingDepthbuffer));
ANGLE_TRY(CopyTextureContentsToStagingBuffer(
contextMtl, textureAngleFormat, angleStagingStencilFormat, region.size, data, bytesPerRow,
&stagingStencilBufferRowPitch, &stagingStencilBuffer2DImageSize, &stagingStencilBuffer));
mtl::BlitCommandEncoder *encoder = contextMtl->getBlitCommandEncoder();
encoder->copyBufferToTexture(stagingDepthbuffer, 0, stagingDepthBufferRowPitch,
stagingDepthBuffer2DImageSize, region.size, texture, slice,
mipmapLevel, region.origin, MTLBlitOptionDepthFromDepthStencil);
encoder->copyBufferToTexture(stagingStencilBuffer, 0, stagingStencilBufferRowPitch,
stagingStencilBuffer2DImageSize, region.size, texture, slice,
mipmapLevel, region.origin, MTLBlitOptionStencilFromDepthStencil);
return angle::Result::Continue;
}
angle::Result UploadTextureContents(const gl::Context *context,
const mtl::TextureRef &texture,
const angle::Format &textureAngleFormat,
const MTLRegion &region,
uint32_t mipmapLevel,
uint32_t slice,
const uint8_t *data,
size_t bytesPerRow)
{
ASSERT(texture && texture->valid());
ContextMtl *contextMtl = mtl::GetImpl(context);
if (texture->isCPUAccessible())
{
// If texture is CPU accessible, just call replaceRegion() directly.
texture->replaceRegion(contextMtl, region, mipmapLevel, slice, data, bytesPerRow);
return angle::Result::Continue;
}
// Texture is not CPU accessible, we need to use staging buffer
if (textureAngleFormat.depthBits && textureAngleFormat.stencilBits)
{
ANGLE_TRY(UploadPackedDepthStencilTextureContentsWithStagingBuffer(
contextMtl, texture, textureAngleFormat, region, mipmapLevel, slice, data,
bytesPerRow));
}
else
{
ANGLE_TRY(UploadTextureContentsWithStagingBuffer(contextMtl, texture, textureAngleFormat,
region, mipmapLevel, slice, data,
bytesPerRow));
}
return angle::Result::Continue;
}
} // namespace
// TextureMtl implementation
@@ -123,8 +325,6 @@ TextureMtl::~TextureMtl() = default;
void TextureMtl::onDestroy(const gl::Context *context)
{
mMetalSamplerState = nil;
releaseTexture(true);
}
@@ -132,7 +332,8 @@ void TextureMtl::releaseTexture(bool releaseImages)
{
mFormat = mtl::Format();
mNativeTexture = nullptr;
mNativeTexture = nullptr;
mMetalSamplerState = nil;
for (RenderTargetMtl &rt : mLayeredRenderTargets)
{
@@ -229,6 +430,42 @@ angle::Result TextureMtl::ensureTextureCreated(const gl::Context *context)
}
}
// Create sampler state
ANGLE_TRY(ensureSamplerStateCreated(context));
return angle::Result::Continue;
}
angle::Result TextureMtl::ensureSamplerStateCreated(const gl::Context *context)
{
if (mMetalSamplerState)
{
return angle::Result::Continue;
}
ContextMtl *contextMtl = mtl::GetImpl(context);
DisplayMtl *displayMtl = contextMtl->getDisplay();
mtl::SamplerDesc samplerDesc(mState.getSamplerState());
if (mFormat.actualAngleFormat().depthBits &&
!displayMtl->getFeatures().hasDepthTextureFiltering.enabled)
{
// On devices not supporting filtering for depth textures, we need to convert to nearest
// here.
samplerDesc.minFilter = MTLSamplerMinMagFilterNearest;
samplerDesc.magFilter = MTLSamplerMinMagFilterNearest;
if (samplerDesc.mipFilter != MTLSamplerMipFilterNotMipmapped)
{
samplerDesc.mipFilter = MTLSamplerMipFilterNearest;
}
samplerDesc.maxAnisotropy = 1;
}
mMetalSamplerState =
displayMtl->getStateCache().getSamplerState(displayMtl->getMetalDevice(), samplerDesc);
return angle::Result::Continue;
}
@@ -508,8 +745,9 @@ angle::Result TextureMtl::generateMipmapCPU(const gl::Context *context)
dstLevelData.get(), dstRowPitch, 0);
// Upload to texture
mNativeTexture->replaceRegion(contextMtl, MTLRegionMake2D(0, 0, dstWidth, dstHeight),
mip, layer, dstLevelData.get(), dstRowPitch);
ANGLE_TRY(UploadTextureContents(context, mNativeTexture, angleFormat,
MTLRegionMake2D(0, 0, dstWidth, dstHeight), mip, layer,
dstLevelData.get(), dstRowPitch));
prevLevelWidth = dstWidth;
prevLevelHeight = dstHeight;
@@ -576,27 +814,14 @@ angle::Result TextureMtl::getAttachmentRenderTarget(const gl::Context *context,
angle::Result TextureMtl::syncState(const gl::Context *context,
const gl::Texture::DirtyBits &dirtyBits)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
if (dirtyBits.none() && mMetalSamplerState)
if (dirtyBits.any())
{
return angle::Result::Continue;
}
DisplayMtl *display = contextMtl->getDisplay();
if (dirtyBits.test(gl::Texture::DIRTY_BIT_SWIZZLE_RED) ||
dirtyBits.test(gl::Texture::DIRTY_BIT_SWIZZLE_GREEN) ||
dirtyBits.test(gl::Texture::DIRTY_BIT_SWIZZLE_BLUE) ||
dirtyBits.test(gl::Texture::DIRTY_BIT_SWIZZLE_ALPHA))
{
// NOTE(hqle): Metal doesn't support swizzle on many devices. Skip for now.
// Invalidate sampler state
mMetalSamplerState = nil;
}
ANGLE_TRY(ensureTextureCreated(context));
mMetalSamplerState = display->getStateCache().getSamplerState(
display->getMetalDevice(), mtl::SamplerDesc(mState.getSamplerState()));
ANGLE_TRY(ensureSamplerStateCreated(context));
return angle::Result::Continue;
}
@@ -812,7 +1037,8 @@ angle::Result TextureMtl::setSubImageImpl(const gl::Context *context,
}
// Upload to texture
image->replaceRegion(contextMtl, mtlRegion, 0, 0, pixels, sourceRowPitch);
ANGLE_TRY(UploadTextureContents(context, image, mFormat.actualAngleFormat(), mtlRegion, 0, 0,
pixels, sourceRowPitch));
return angle::Result::Continue;
}
@@ -852,7 +1078,8 @@ angle::Result TextureMtl::convertAndSetSubImage(const gl::Context *context,
false, false, false);
// Upload to texture
image->replaceRegion(contextMtl, mtlRow, 0, 0, conversionRow.data(), dstRowPitch);
ANGLE_TRY(UploadTextureContents(context, image, dstFormat, mtlRow, 0, 0,
conversionRow.data(), dstRowPitch));
}
return angle::Result::Continue;
@@ -1019,7 +1246,8 @@ angle::Result TextureMtl::copySubImageCPU(const gl::Context *context,
conversionRow.data()));
// Upload to texture
image->replaceRegion(contextMtl, mtlDstRowArea, 0, 0, conversionRow.data(), dstRowPitch);
ANGLE_TRY(UploadTextureContents(context, image, dstFormat, mtlDstRowArea, 0, 0,
conversionRow.data(), dstRowPitch));
}
return angle::Result::Continue;

View File

@@ -274,6 +274,17 @@ class BlitCommandEncoder final : public CommandEncoder
BlitCommandEncoder &restart();
BlitCommandEncoder &copyBufferToTexture(const BufferRef &src,
size_t srcOffset,
size_t srcBytesPerRow,
size_t srcBytesPerImage,
MTLSize srcSize,
const TextureRef &dst,
uint32_t dstSlice,
uint32_t dstLevel,
MTLOrigin dstOrigin,
MTLBlitOption blitOption);
BlitCommandEncoder &copyTexture(const TextureRef &dst,
uint32_t dstSlice,
uint32_t dstLevel,

View File

@@ -840,6 +840,39 @@ BlitCommandEncoder &BlitCommandEncoder::restart()
}
}
BlitCommandEncoder &BlitCommandEncoder::copyBufferToTexture(const BufferRef &src,
size_t srcOffset,
size_t srcBytesPerRow,
size_t srcBytesPerImage,
MTLSize srcSize,
const TextureRef &dst,
uint32_t dstSlice,
uint32_t dstLevel,
MTLOrigin dstOrigin,
MTLBlitOption blitOption)
{
if (!src || !dst)
{
return *this;
}
cmdBuffer().setReadDependency(src);
cmdBuffer().setWriteDependency(dst);
[get() copyFromBuffer:src->get()
sourceOffset:srcOffset
sourceBytesPerRow:srcBytesPerRow
sourceBytesPerImage:srcBytesPerImage
sourceSize:srcSize
toTexture:dst->get()
destinationSlice:dstSlice
destinationLevel:dstLevel
destinationOrigin:dstOrigin
options:blitOption];
return *this;
}
BlitCommandEncoder &BlitCommandEncoder::copyTexture(const TextureRef &dst,
uint32_t dstSlice,
uint32_t dstLevel,

View File

@@ -323,6 +323,10 @@ class Context : public ErrorHandler
#define ANGLE_MTL_TRY(context, test) ANGLE_MTL_CHECK(context, test, GL_INVALID_OPERATION)
#define ANGLE_MTL_UNREACHABLE(context) \
UNREACHABLE(); \
ANGLE_MTL_TRY(context, false)
} // namespace mtl
} // namespace rx

View File

@@ -69,7 +69,8 @@
"A32_FLOAT": "R32G32B32A32_FLOAT",
"L32_FLOAT": "R32G32B32A32_FLOAT",
"L32A32_FLOAT": "R32G32B32A32_FLOAT",
"D24_UNORM_X8_UINT": "D32_FLOAT"
"D24_UNORM_X8_UINT": "D32_FLOAT",
"D32_UNORM": "D32_FLOAT"
},
"override_ios": {
"D24_UNORM_S8_UINT": "D32_FLOAT_S8X24_UINT",

View File

@@ -262,6 +262,11 @@ void Format::init(const DisplayMtl *display, angle::FormatID intendedFormatId_)
this->actualFormatId = angle::FormatID::D32_FLOAT;
break;
case angle::FormatID::D32_UNORM:
this->metalFormat = MTLPixelFormatDepth32Float;
this->actualFormatId = angle::FormatID::D32_FLOAT;
break;
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
case angle::FormatID::L16A16_FLOAT:
if (metalDevice.depth24Stencil8PixelFormatSupported)

View File

@@ -34,6 +34,14 @@ bool OverrideTextureCaps(const DisplayMtl *display, angle::FormatID formatId, gl
case angle::FormatID::R8G8B8A8_UNORM_SRGB:
case angle::FormatID::B8G8R8A8_UNORM:
case angle::FormatID::B8G8R8A8_UNORM_SRGB:
// NOTE: even though iOS devices don't support filtering depth textures, we still report as
// supported here in order for the OES_depth_texture extension to be enabled.
// During draw call, the filter modes will be converted to nearest.
case angle::FormatID::D16_UNORM:
case angle::FormatID::D24_UNORM_S8_UINT:
case angle::FormatID::D32_FLOAT_S8X24_UINT:
case angle::FormatID::D32_FLOAT:
case angle::FormatID::D32_UNORM:
caps->texturable = caps->filterable = caps->textureAttachment = caps->renderbuffer =
true;
return true;
@@ -112,11 +120,13 @@ void GenerateTextureCapsMap(const FormatTable &formatTable,
tmpTextureExtensions.compressedTexturePVRTCsRGB = true;
}
#endif
tmpTextureExtensions.sRGB = true;
tmpTextureExtensions.depth32 = true;
tmpTextureExtensions.depth24OES = true;
tmpTextureExtensions.rgb8rgba8 = true;
tmpTextureExtensions.textureStorage = true;
tmpTextureExtensions.sRGB = true;
tmpTextureExtensions.depth32 = true;
tmpTextureExtensions.depth24OES = true;
tmpTextureExtensions.rgb8rgba8 = true;
tmpTextureExtensions.textureStorage = true;
tmpTextureExtensions.depthTextureOES = true;
tmpTextureExtensions.depthTextureANGLE = true;
auto formatVerifier = [&](const gl::InternalFormat &internalFormatInfo) {
angle::FormatID angleFormatId =
@@ -146,14 +156,6 @@ void GenerateTextureCapsMap(const FormatTable &formatTable,
textureCaps.sampleCounts.insert(0);
textureCaps.sampleCounts.insert(1);
if (textureCaps.filterable && mtlFormat.actualFormatId == angle::FormatID::D32_FLOAT)
{
// Only MacOS support filterable for D32_FLOAT texture
#if !TARGET_OS_OSX || TARGET_OS_MACCATALYST
textureCaps.filterable = false;
#endif
}
textureCapsMap.set(mtlFormat.intendedFormatId, textureCaps);
if (intendedAngleFormat.isBlock)

View File

@@ -112,6 +112,9 @@ class Texture final : public Resource,
static TextureRef MakeFromMetal(id<MTLTexture> metalTexture);
// Allow CPU to read & write data directly to this texture?
bool isCPUAccessible() const;
void replaceRegion(ContextMtl *context,
MTLRegion region,
uint32_t mipmapLevel,
@@ -130,6 +133,8 @@ class Texture final : public Resource,
TextureRef createCubeFaceView(uint32_t face);
// Create a view of one slice at a level.
TextureRef createSliceMipView(uint32_t slice, uint32_t level);
// Create a view with different format
TextureRef createViewWithDifferentFormat(MTLPixelFormat format);
MTLTextureType textureType() const;
MTLPixelFormat pixelFormat() const;
@@ -163,6 +168,7 @@ class Texture final : public Resource,
bool supportTextureView);
// Create a texture view
Texture(Texture *original, MTLPixelFormat format);
Texture(Texture *original, MTLTextureType type, NSRange mipmapLevelRange, uint32_t slice);
void syncContent(ContextMtl *context);

View File

@@ -201,6 +201,18 @@ Texture::Texture(ContextMtl *context,
}
}
Texture::Texture(Texture *original, MTLPixelFormat format)
: Resource(original),
mColorWritableMask(original->mColorWritableMask) // Share color write mask property
{
ANGLE_MTL_OBJC_SCOPE
{
auto view = [original->get() newTextureViewWithPixelFormat:format];
set([view ANGLE_MTL_AUTORELEASE]);
}
}
Texture::Texture(Texture *original, MTLTextureType type, NSRange mipmapLevelRange, uint32_t slice)
: Resource(original),
mColorWritableMask(original->mColorWritableMask) // Share color write mask property
@@ -242,6 +254,17 @@ void Texture::syncContent(ContextMtl *context)
#endif
}
bool Texture::isCPUAccessible() const
{
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
if (get().storageMode == MTLStorageModeManaged)
{
return true;
}
#endif
return get().storageMode == MTLStorageModeShared;
}
void Texture::replaceRegion(ContextMtl *context,
MTLRegion region,
uint32_t mipmapLevel,
@@ -253,6 +276,9 @@ void Texture::replaceRegion(ContextMtl *context,
{
return;
}
ASSERT(isCPUAccessible());
CommandQueue &cmdQueue = context->cmdQueue();
syncContent(context);
@@ -279,6 +305,8 @@ void Texture::getBytes(ContextMtl *context,
uint32_t mipmapLevel,
uint8_t *dataOut)
{
ASSERT(isCPUAccessible());
CommandQueue &cmdQueue = context->cmdQueue();
syncContent(context);
@@ -327,6 +355,11 @@ TextureRef Texture::createSliceMipView(uint32_t slice, uint32_t level)
}
}
TextureRef Texture::createViewWithDifferentFormat(MTLPixelFormat format)
{
return TextureRef(new Texture(this, format));
}
MTLPixelFormat Texture::pixelFormat() const
{
return get().pixelFormat;

View File

@@ -48,6 +48,10 @@ struct StencilDesc
struct DepthStencilDesc
{
DepthStencilDesc();
DepthStencilDesc(const DepthStencilDesc &src);
DepthStencilDesc(DepthStencilDesc &&src);
DepthStencilDesc &operator=(const DepthStencilDesc &src);
bool operator==(const DepthStencilDesc &rhs) const;
@@ -78,9 +82,13 @@ struct DepthStencilDesc
struct SamplerDesc
{
SamplerDesc();
SamplerDesc(const SamplerDesc &src);
SamplerDesc(SamplerDesc &&src);
explicit SamplerDesc(const gl::SamplerState &glState);
SamplerDesc &operator=(const SamplerDesc &src);
// Set default values. All filters are nearest, and addresModes are clamp to edge.
void reset();
@@ -207,6 +215,10 @@ constexpr PrimitiveTopologyClass kPrimitiveTopologyClassPoint = MTLPrimitiveTopo
struct RenderPipelineDesc
{
RenderPipelineDesc();
RenderPipelineDesc(const RenderPipelineDesc &src);
RenderPipelineDesc(RenderPipelineDesc &&src);
RenderPipelineDesc &operator=(const RenderPipelineDesc &src);
bool operator==(const RenderPipelineDesc &rhs) const;

View File

@@ -245,6 +245,20 @@ DepthStencilDesc::DepthStencilDesc()
{
memset(this, 0, sizeof(*this));
}
DepthStencilDesc::DepthStencilDesc(const DepthStencilDesc &src)
{
memcpy(this, &src, sizeof(*this));
}
DepthStencilDesc::DepthStencilDesc(DepthStencilDesc &&src)
{
memcpy(this, &src, sizeof(*this));
}
DepthStencilDesc &DepthStencilDesc::operator=(const DepthStencilDesc &src)
{
memcpy(this, &src, sizeof(*this));
return *this;
}
bool DepthStencilDesc::operator==(const DepthStencilDesc &rhs) const
{
@@ -388,6 +402,14 @@ SamplerDesc::SamplerDesc()
{
memset(this, 0, sizeof(*this));
}
SamplerDesc::SamplerDesc(const SamplerDesc &src)
{
memcpy(this, &src, sizeof(*this));
}
SamplerDesc::SamplerDesc(SamplerDesc &&src)
{
memcpy(this, &src, sizeof(*this));
}
SamplerDesc::SamplerDesc(const gl::SamplerState &glState) : SamplerDesc()
{
@@ -402,6 +424,12 @@ SamplerDesc::SamplerDesc(const gl::SamplerState &glState) : SamplerDesc()
maxAnisotropy = static_cast<uint32_t>(glState.getMaxAnisotropy());
}
SamplerDesc &SamplerDesc::operator=(const SamplerDesc &src)
{
memcpy(this, &src, sizeof(*this));
return *this;
}
void SamplerDesc::reset()
{
rAddressMode = MTLSamplerAddressModeClampToEdge;
@@ -570,12 +598,28 @@ RenderPipelineDesc::RenderPipelineDesc()
rasterizationEnabled = true;
}
RenderPipelineDesc::RenderPipelineDesc(const RenderPipelineDesc &src)
{
memcpy(this, &src, sizeof(*this));
}
RenderPipelineDesc::RenderPipelineDesc(RenderPipelineDesc &&src)
{
memcpy(this, &src, sizeof(*this));
}
RenderPipelineDesc &RenderPipelineDesc::operator=(const RenderPipelineDesc &src)
{
memcpy(this, &src, sizeof(*this));
return *this;
}
bool RenderPipelineDesc::operator==(const RenderPipelineDesc &rhs) const
{
return ANGLE_PROP_EQ(*this, rhs, vertexDescriptor) &&
ANGLE_PROP_EQ(*this, rhs, outputDescriptor) &&
ANGLE_PROP_EQ(*this, rhs, inputPrimitiveTopology);
// NOTE(hqle): Use a faster way to compare, i.e take into account
// the number of active vertex attributes & render targets.
// If that way is used, hash() method must be changed also.
return memcmp(this, &rhs, sizeof(*this)) == 0;
}
size_t RenderPipelineDesc::hash() const

View File

@@ -704,14 +704,14 @@ TEST_P(DepthStencilFormatsTest, VerifyDepthStencilUploadData)
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc(GL_EQUAL, kStencilRef, 0xFF);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
glClear(GL_COLOR_BUFFER_BIT);
drawQuad(program.get(), essl1_shaders::PositionAttrib(), 1.0f);
ASSERT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, fb);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::red);
ASSERT_GL_NO_ERROR();
@@ -735,6 +735,152 @@ TEST_P(DepthStencilFormatsTest, VerifyDepthStencilUploadData)
EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::black);
}
// Verify that depth texture's data can be uploaded correctly
TEST_P(DepthStencilFormatsTest, VerifyDepth32UploadData)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_depth_texture"));
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
ASSERT_GL_NO_ERROR();
// normalized 0.99 = 0xfd70a3d6
std::vector<GLuint> depthData(1, 0xfd70a3d6);
GLTexture rbDepth;
glBindTexture(GL_TEXTURE_2D, rbDepth);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 1, 1, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT,
depthData.data());
ASSERT_GL_NO_ERROR();
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rbDepth, 0);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ASSERT_GL_NO_ERROR();
ANGLE_GL_PROGRAM(programRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_GEQUAL);
glClearColor(0, 1, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
// Fail Depth Test and color buffer is unchanged
float depthValue = 0.98f;
drawQuad(programRed.get(), essl1_shaders::PositionAttrib(), depthValue * 2 - 1);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
// Pass Depth Test and draw red
depthValue = 1.0f;
drawQuad(programRed.get(), essl1_shaders::PositionAttrib(), depthValue * 2 - 1);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
ASSERT_GL_NO_ERROR();
// Change depth texture data
glBindTexture(GL_TEXTURE_2D, rbDepth);
depthData[0] = 0x7fffffff; // 0.5
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT,
depthData.data());
ASSERT_GL_NO_ERROR();
ANGLE_GL_PROGRAM(programGreen, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
// Fail Depth Test and color buffer is unchanged
depthValue = 0.48f;
drawQuad(programGreen.get(), essl1_shaders::PositionAttrib(), depthValue * 2 - 1);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
ASSERT_GL_NO_ERROR();
ANGLE_GL_PROGRAM(programBlue, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
glClearDepthf(0.0f);
glClear(GL_DEPTH_BUFFER_BIT);
// Pass Depth Test and draw blue
depthValue = 0.01f;
drawQuad(programBlue.get(), essl1_shaders::PositionAttrib(), depthValue * 2 - 1);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
glDisable(GL_DEPTH_TEST);
ASSERT_GL_NO_ERROR();
}
// Verify that 16 bits depth texture's data can be uploaded correctly
TEST_P(DepthStencilFormatsTest, VerifyDepth16UploadData)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_depth_texture"));
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
ASSERT_GL_NO_ERROR();
// normalized 0.99 = 0xfd6f
std::vector<GLushort> depthData(1, 0xfd6f);
GLTexture rbDepth;
glBindTexture(GL_TEXTURE_2D, rbDepth);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 1, 1, 0, GL_DEPTH_COMPONENT,
GL_UNSIGNED_SHORT, depthData.data());
ASSERT_GL_NO_ERROR();
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rbDepth, 0);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ASSERT_GL_NO_ERROR();
ANGLE_GL_PROGRAM(programRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_GEQUAL);
glClearColor(0, 1, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
// Fail Depth Test and color buffer is unchanged
float depthValue = 0.98f;
drawQuad(programRed.get(), essl1_shaders::PositionAttrib(), depthValue * 2 - 1);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
// Pass Depth Test and draw red
depthValue = 1.0f;
drawQuad(programRed.get(), essl1_shaders::PositionAttrib(), depthValue * 2 - 1);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
ASSERT_GL_NO_ERROR();
// Change depth texture data
glBindTexture(GL_TEXTURE_2D, rbDepth);
depthData[0] = 0x7fff; // 0.5
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT,
depthData.data());
ASSERT_GL_NO_ERROR();
ANGLE_GL_PROGRAM(programGreen, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
// Fail Depth Test and color buffer is unchanged
depthValue = 0.48f;
drawQuad(programGreen.get(), essl1_shaders::PositionAttrib(), depthValue * 2 - 1);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
ASSERT_GL_NO_ERROR();
ANGLE_GL_PROGRAM(programBlue, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
glClearDepthf(0.0f);
glClear(GL_DEPTH_BUFFER_BIT);
// Pass Depth Test and draw blue
depthValue = 0.01f;
drawQuad(programBlue.get(), essl1_shaders::PositionAttrib(), depthValue * 2 - 1);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
glDisable(GL_DEPTH_TEST);
ASSERT_GL_NO_ERROR();
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST_ES2(DepthStencilFormatsTest);