Commit acda9dde by Le Hoang Quyen Committed by Commit Bot

Metal: Implement GL_OES_texture_3D

Bug: angleproject:2634 Change-Id: I8c46493ac28fe1bbfdb29ee3a60b23076bbc4c0c Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2336119 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarJonah Ryan-Davis <jonahr@google.com>
parent f2196ad6
...@@ -133,7 +133,7 @@ angle::Result ContextMtl::ensureIncompleteTexturesCreated(const gl::Context *con ...@@ -133,7 +133,7 @@ angle::Result ContextMtl::ensureIncompleteTexturesCreated(const gl::Context *con
{ {
return angle::Result::Continue; return angle::Result::Continue;
} }
constexpr gl::TextureType supportedTextureTypes[] = {gl::TextureType::_2D, constexpr gl::TextureType supportedTextureTypes[] = {gl::TextureType::_2D, gl::TextureType::_3D,
gl::TextureType::CubeMap}; gl::TextureType::CubeMap};
for (gl::TextureType texType : supportedTextureTypes) for (gl::TextureType texType : supportedTextureTypes)
{ {
......
...@@ -639,7 +639,7 @@ void DisplayMtl::initializeExtensions() const ...@@ -639,7 +639,7 @@ void DisplayMtl::initializeExtensions() const
mNativeExtensions.textureNPOTOES = true; mNativeExtensions.textureNPOTOES = true;
mNativeExtensions.texture3DOES = false; mNativeExtensions.texture3DOES = true;
mNativeExtensions.standardDerivativesOES = true; mNativeExtensions.standardDerivativesOES = true;
......
...@@ -1069,17 +1069,17 @@ angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context, ...@@ -1069,17 +1069,17 @@ angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
mtl::Texture *texture; mtl::TextureRef texture;
if (mBackbuffer) if (mBackbuffer)
{ {
// Backbuffer might have MSAA texture as render target, needs to obtain the // Backbuffer might have MSAA texture as render target, needs to obtain the
// resolved texture to be able to read pixels. // resolved texture to be able to read pixels.
ANGLE_TRY(mBackbuffer->ensureColorTextureReadyForReadPixels(context)); ANGLE_TRY(mBackbuffer->ensureColorTextureReadyForReadPixels(context));
texture = mBackbuffer->getColorTexture().get(); texture = mBackbuffer->getColorTexture();
} }
else else
{ {
texture = renderTarget->getTexture().get(); texture = renderTarget->getTexture();
// For non-default framebuffer, MSAA read pixels is disallowed. // For non-default framebuffer, MSAA read pixels is disallowed.
ANGLE_MTL_CHECK(contextMtl, texture->samples() == 1, GL_INVALID_OPERATION); ANGLE_MTL_CHECK(contextMtl, texture->samples() == 1, GL_INVALID_OPERATION);
} }
...@@ -1087,13 +1087,12 @@ angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context, ...@@ -1087,13 +1087,12 @@ angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context,
const mtl::Format &readFormat = *renderTarget->getFormat(); const mtl::Format &readFormat = *renderTarget->getFormat();
const angle::Format &readAngleFormat = readFormat.actualAngleFormat(); const angle::Format &readAngleFormat = readFormat.actualAngleFormat();
// NOTE(hqle): resolve MSAA texture before readback int bufferRowPitch = area.width * readAngleFormat.pixelBytes;
int srcRowPitch = area.width * readAngleFormat.pixelBytes;
angle::MemoryBuffer readPixelRowBuffer; angle::MemoryBuffer readPixelRowBuffer;
ANGLE_CHECK_GL_ALLOC(contextMtl, readPixelRowBuffer.resize(srcRowPitch)); ANGLE_CHECK_GL_ALLOC(contextMtl, readPixelRowBuffer.resize(bufferRowPitch));
auto packPixelsRowParams = packPixelsParams; auto packPixelsRowParams = packPixelsParams;
MTLRegion mtlSrcRowRegion = MTLRegionMake2D(area.x, area.y, area.width, 1); gl::Rectangle srcRowRegion(area.x, area.y, area.width, 1);
int rowOffset = packPixelsParams.reverseRowOrder ? -1 : 1; int rowOffset = packPixelsParams.reverseRowOrder ? -1 : 1;
int startRow = packPixelsParams.reverseRowOrder ? (area.y1() - 1) : area.y; int startRow = packPixelsParams.reverseRowOrder ? (area.y1() - 1) : area.y;
...@@ -1104,16 +1103,16 @@ angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context, ...@@ -1104,16 +1103,16 @@ angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context,
for (int r = startRow, i = 0; i < area.height; for (int r = startRow, i = 0; i < area.height;
++i, r += rowOffset, pixels += packPixelsRowParams.outputPitch) ++i, r += rowOffset, pixels += packPixelsRowParams.outputPitch)
{ {
mtlSrcRowRegion.origin.y = r; srcRowRegion.y = r;
packPixelsRowParams.area.y = packPixelsParams.area.y + i; packPixelsRowParams.area.y = packPixelsParams.area.y + i;
// Read the pixels data to the row buffer // Read the pixels data to the row buffer
texture->getBytes(contextMtl, srcRowPitch, mtlSrcRowRegion, ANGLE_TRY(mtl::ReadTexturePerSliceBytes(
static_cast<uint32_t>(renderTarget->getLevelIndex()), context, texture, bufferRowPitch, srcRowRegion, renderTarget->getLevelIndex(),
readPixelRowBuffer.data()); renderTarget->getLayerIndex(), readPixelRowBuffer.data()));
// Convert to destination format // Convert to destination format
PackPixels(packPixelsRowParams, readAngleFormat, srcRowPitch, readPixelRowBuffer.data(), PackPixels(packPixelsRowParams, readAngleFormat, bufferRowPitch, readPixelRowBuffer.data(),
pixels); pixels);
} }
......
...@@ -21,6 +21,13 @@ ...@@ -21,6 +21,13 @@
namespace rx namespace rx
{ {
// structure represents one image definition of a texture created by glTexImage* call.
struct ImageDefinitionMtl
{
mtl::TextureRef image;
angle::FormatID formatID = angle::FormatID::NONE;
};
class TextureMtl : public TextureImpl class TextureMtl : public TextureImpl
{ {
public: public:
...@@ -162,12 +169,25 @@ class TextureMtl : public TextureImpl ...@@ -162,12 +169,25 @@ class TextureMtl : public TextureImpl
private: private:
void releaseTexture(bool releaseImages); void releaseTexture(bool releaseImages);
void releaseTexture(bool releaseImages, bool releaseTextureObjectsOnly);
angle::Result createNativeTexture(const gl::Context *context,
gl::TextureType type,
GLuint mips,
const gl::Extents &size);
angle::Result ensureSamplerStateCreated(const gl::Context *context); angle::Result ensureSamplerStateCreated(const gl::Context *context);
// Ensure image at given index is created: // Ensure image at given index is created:
angle::Result ensureImageCreated(const gl::Context *context, const gl::ImageIndex &index); angle::Result ensureImageCreated(const gl::Context *context, const gl::ImageIndex &index);
// Ensure all image views at all faces/levels are retained.
void retainImageDefinitions();
mtl::TextureRef createImageViewFromNativeTexture(GLuint cubeFaceOrZero, GLuint nativeLevel);
angle::Result checkForEmulatedChannels(const gl::Context *context, angle::Result checkForEmulatedChannels(const gl::Context *context,
const mtl::Format &mtlFormat, const mtl::Format &mtlFormat,
const mtl::TextureRef &texture); const mtl::TextureRef &texture);
int getNativeLevel(const gl::ImageIndex &imageIndex) const;
mtl::TextureRef &getImage(const gl::ImageIndex &imageIndex);
ImageDefinitionMtl &getImageDefinition(const gl::ImageIndex &imageIndex);
RenderTargetMtl &getRenderTarget(const gl::ImageIndex &imageIndex);
bool isIndexWithinMinMaxLevels(const gl::ImageIndex &imageIndex) const;
// If levels = 0, this function will create full mipmaps texture. // If levels = 0, this function will create full mipmaps texture.
angle::Result setStorageImpl(const gl::Context *context, angle::Result setStorageImpl(const gl::Context *context,
...@@ -183,10 +203,12 @@ class TextureMtl : public TextureImpl ...@@ -183,10 +203,12 @@ class TextureMtl : public TextureImpl
angle::Result setImageImpl(const gl::Context *context, angle::Result setImageImpl(const gl::Context *context,
const gl::ImageIndex &index, const gl::ImageIndex &index,
const gl::InternalFormat &formatInfo, const gl::InternalFormat &dstFormatInfo,
const gl::Extents &size, const gl::Extents &size,
GLenum type, GLenum srcFormat,
GLenum srcType,
const gl::PixelUnpackState &unpack, const gl::PixelUnpackState &unpack,
gl::Buffer *unpackBuffer,
const uint8_t *pixels); const uint8_t *pixels);
angle::Result setSubImageImpl(const gl::Context *context, angle::Result setSubImageImpl(const gl::Context *context,
const gl::ImageIndex &index, const gl::ImageIndex &index,
...@@ -194,6 +216,7 @@ class TextureMtl : public TextureImpl ...@@ -194,6 +216,7 @@ class TextureMtl : public TextureImpl
const gl::InternalFormat &formatInfo, const gl::InternalFormat &formatInfo,
GLenum type, GLenum type,
const gl::PixelUnpackState &unpack, const gl::PixelUnpackState &unpack,
gl::Buffer *unpackBuffer,
const uint8_t *pixels); const uint8_t *pixels);
angle::Result copySubImageImpl(const gl::Context *context, angle::Result copySubImageImpl(const gl::Context *context,
...@@ -250,15 +273,33 @@ class TextureMtl : public TextureImpl ...@@ -250,15 +273,33 @@ class TextureMtl : public TextureImpl
bool unpackUnmultiplyAlpha, bool unpackUnmultiplyAlpha,
const mtl::TextureRef &sourceTexture); const mtl::TextureRef &sourceTexture);
// Copy data to texture's per array's slice/cube's face. NOTE: This function doesn't upload
// data to 3D texture's z layer. Metal treats 3D texture's z layer & array texture's slice
// differently. For array/cube texture, it is only possible to upload to one slice at a time.
angle::Result setPerSliceSubImage(const gl::Context *context,
int slice,
const MTLRegion &mtlArea,
const gl::InternalFormat &internalFormat,
GLenum type,
const angle::Format &pixelsAngleFormat,
size_t pixelsRowPitch,
size_t pixelsDepthPitch,
gl::Buffer *unpackBuffer,
const uint8_t *pixels,
const mtl::TextureRef &image);
// Convert pixels to suported format before uploading to texture // Convert pixels to suported format before uploading to texture
angle::Result convertAndSetSubImage(const gl::Context *context, angle::Result convertAndSetPerSliceSubImage(const gl::Context *context,
const gl::ImageIndex &index, int slice,
const MTLRegion &mtlArea, const MTLRegion &mtlArea,
const gl::InternalFormat &internalFormat, const gl::InternalFormat &internalFormat,
GLenum type, GLenum type,
const angle::Format &pixelsFormat, const angle::Format &pixelsAngleFormat,
size_t pixelsRowPitch, size_t pixelsRowPitch,
const uint8_t *pixels); size_t pixelsDepthPitch,
gl::Buffer *unpackBuffer,
const uint8_t *pixels,
const mtl::TextureRef &image);
angle::Result generateMipmapCPU(const gl::Context *context); angle::Result generateMipmapCPU(const gl::Context *context);
...@@ -267,12 +308,23 @@ class TextureMtl : public TextureImpl ...@@ -267,12 +308,23 @@ class TextureMtl : public TextureImpl
mtl::TextureRef mNativeTexture; mtl::TextureRef mNativeTexture;
id<MTLSamplerState> mMetalSamplerState = nil; id<MTLSamplerState> mMetalSamplerState = nil;
std::vector<RenderTargetMtl> mLayeredRenderTargets; // Number of slices
std::vector<mtl::TextureRef> mLayeredTextureViews; uint32_t mSlices = 1;
// Stored images array defined by glTexImage/glCopy*. // Stored images array defined by glTexImage/glCopy*.
// Once the images array is complete, they will be transferred to real texture object. // Once the images array is complete, they will be transferred to real texture object.
std::map<int, gl::TexLevelArray<mtl::TextureRef>> mTexImages; // NOTE:
// - For Cube map, there will be at most 6 entries in the mTexImageDefs table, one for each
// face. This is because the Cube map's image is defined per face & per level.
// - For other texture types, there will be only one entry in the map table. All other textures
// except Cube map has texture image defined per level (all slices included).
std::map<int, gl::TexLevelArray<ImageDefinitionMtl>> mTexImageDefs;
// Render Target per slice/depth/cube face.
// - For 2D texture: There will be one key entry in the map.
// - For Cube map: There will be at most 6 key entries.
// - For array/3D texture: There will be at most slices/depths number of key entries.
std::map<int, gl::TexLevelArray<RenderTargetMtl>> mPerLayerRenderTargets;
bool mIsPow2 = false; bool mIsPow2 = false;
}; };
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "common/MemoryBuffer.h" #include "common/MemoryBuffer.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/mathutil.h" #include "common/mathutil.h"
#include "image_util/imageformats.h"
#include "libANGLE/Surface.h" #include "libANGLE/Surface.h"
#include "libANGLE/renderer/metal/ContextMtl.h" #include "libANGLE/renderer/metal/ContextMtl.h"
#include "libANGLE/renderer/metal/DisplayMtl.h" #include "libANGLE/renderer/metal/DisplayMtl.h"
...@@ -30,20 +31,53 @@ namespace rx ...@@ -30,20 +31,53 @@ namespace rx
namespace namespace
{ {
gl::ImageIndex GetImageBaseLevelIndex(const mtl::TextureRef &image) gl::ImageIndex GetZeroLevelIndex(const mtl::TextureRef &image)
{ {
gl::ImageIndex imageBaseIndex;
switch (image->textureType()) switch (image->textureType())
{ {
case MTLTextureType2D: case MTLTextureType2D:
imageBaseIndex = gl::ImageIndex::Make2D(0); return gl::ImageIndex::Make2D(0);
case MTLTextureTypeCube:
return gl::ImageIndex::MakeFromType(gl::TextureType::CubeMap, 0);
case MTLTextureType2DArray:
return gl::ImageIndex::Make2DArray(0 /** entire layers */);
case MTLTextureType2DMultisample:
return gl::ImageIndex::Make2DMultisample();
case MTLTextureType3D:
return gl::ImageIndex::Make3D(0 /** entire layers */);
default:
UNREACHABLE();
break; break;
}
return gl::ImageIndex();
}
// Slice is ignored if texture type is not Cube or 2D array
gl::ImageIndex GetSliceMipIndex(const mtl::TextureRef &image, uint32_t slice, uint32_t level)
{
switch (image->textureType())
{
case MTLTextureType2D:
return gl::ImageIndex::Make2D(level);
case MTLTextureTypeCube:
{
auto cubeFace = static_cast<gl::TextureTarget>(
static_cast<int>(gl::TextureTarget::CubeMapPositiveX) + slice);
return gl::ImageIndex::MakeCubeMapFace(cubeFace, level);
}
case MTLTextureType2DArray:
return gl::ImageIndex::Make2DArray(level, slice);
case MTLTextureType2DMultisample:
return gl::ImageIndex::Make2DMultisample();
case MTLTextureType3D:
return gl::ImageIndex::Make3D(level);
default: default:
UNREACHABLE(); UNREACHABLE();
break; break;
} }
return imageBaseIndex; return gl::ImageIndex();
} }
GLuint GetImageLayerIndex(const gl::ImageIndex &index) GLuint GetImageLayerIndex(const gl::ImageIndex &index)
...@@ -51,9 +85,14 @@ GLuint GetImageLayerIndex(const gl::ImageIndex &index) ...@@ -51,9 +85,14 @@ GLuint GetImageLayerIndex(const gl::ImageIndex &index)
switch (index.getType()) switch (index.getType())
{ {
case gl::TextureType::_2D: case gl::TextureType::_2D:
case gl::TextureType::_2DMultisample:
case gl::TextureType::Rectangle:
return 0; return 0;
case gl::TextureType::CubeMap: case gl::TextureType::CubeMap:
return index.cubeMapFaceIndex(); return index.cubeMapFaceIndex();
case gl::TextureType::_2DArray:
case gl::TextureType::_3D:
return index.getLayerIndex();
default: default:
UNREACHABLE(); UNREACHABLE();
} }
...@@ -61,79 +100,131 @@ GLuint GetImageLayerIndex(const gl::ImageIndex &index) ...@@ -61,79 +100,131 @@ GLuint GetImageLayerIndex(const gl::ImageIndex &index)
return 0; return 0;
} }
// Given texture type, get texture type of one image at a slice and a level. GLuint GetImageCubeFaceIndexOrZero(const gl::ImageIndex &index)
{
switch (index.getType())
{
case gl::TextureType::CubeMap:
return index.cubeMapFaceIndex();
default:
break;
}
return 0;
}
// Given texture type, get texture type of one image for a glTexImage call.
// For example, for texture 2d, one image is also texture 2d. // For example, for texture 2d, one image is also texture 2d.
// for texture cube, one image is texture 2d. // for texture cube, one image is texture 2d.
// For texture 2d array, one image is texture 2d.
gl::TextureType GetTextureImageType(gl::TextureType texType) gl::TextureType GetTextureImageType(gl::TextureType texType)
{ {
switch (texType) switch (texType)
{ {
case gl::TextureType::_2D:
case gl::TextureType::CubeMap: case gl::TextureType::CubeMap:
return gl::TextureType::_2D; return gl::TextureType::_2D;
case gl::TextureType::_2D:
case gl::TextureType::_2DArray:
case gl::TextureType::_2DMultisample:
case gl::TextureType::_3D:
case gl::TextureType::Rectangle:
return texType;
default: default:
UNREACHABLE(); UNREACHABLE();
return gl::TextureType::InvalidEnum; return gl::TextureType::InvalidEnum;
} }
} }
angle::Result CopyTextureContentsToStagingBuffer(ContextMtl *contextMtl, // D24X8 by default writes depth data to high 24 bits of 32 bit integers. However, Metal separate
const angle::Format &textureAngleFormat, // depth stencil blitting expects depth data to be in low 24 bits of the data.
const angle::Format &stagingAngleFormat, void WriteDepthStencilToDepth24(const uint8_t *srcPtr, uint8_t *dstPtr)
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. auto src = reinterpret_cast<const angle::DepthStencil *>(srcPtr);
ASSERT(regionSize.depth == 1); auto dst = reinterpret_cast<uint32_t *>(dstPtr);
*dst = gl::floatToNormalized<24, uint32_t>(static_cast<float>(src->depth));
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) void ConvertDepthStencilData(const MTLSize &regionSize,
const angle::Format &srcAngleFormat,
size_t srcRowPitch,
size_t src2DImageSize,
const uint8_t *psrc,
const angle::Format &dstAngleFormat,
rx::PixelWriteFunction pixelWriteFunctionOverride,
size_t destRowPitch,
size_t dest2DImageSize,
uint8_t *pdst)
{
if (srcAngleFormat.id == dstAngleFormat.id)
{
size_t rowCopySize = std::min(srcRowPitch, destRowPitch);
for (NSUInteger d = 0; d < regionSize.depth; ++d)
{ {
for (NSUInteger r = 0; r < regionSize.height; ++r) for (NSUInteger r = 0; r < regionSize.height; ++r)
{ {
const uint8_t *pCopySrc = data + r * bytesPerRow; const uint8_t *pCopySrc = psrc + d * src2DImageSize + r * srcRowPitch;
uint8_t *pCopyDst = pdst + r * stagingBufferRowPitch; uint8_t *pCopyDst = pdst + d * dest2DImageSize + r * destRowPitch;
memcpy(pCopyDst, pCopySrc, stagingBufferRowPitch); memcpy(pCopyDst, pCopySrc, rowCopySize);
}
} }
} }
else else
{ {
rx::PixelWriteFunction pixelWriteFunction = pixelWriteFunctionOverride
? pixelWriteFunctionOverride
: dstAngleFormat.pixelWriteFunction;
// This is only for depth & stencil case. // This is only for depth & stencil case.
ASSERT(textureAngleFormat.depthBits || textureAngleFormat.stencilBits); ASSERT(srcAngleFormat.depthBits || srcAngleFormat.stencilBits);
ASSERT(textureAngleFormat.pixelReadFunction && stagingAngleFormat.pixelWriteFunction); ASSERT(srcAngleFormat.pixelReadFunction && pixelWriteFunction);
// cache to store read result of source pixel // cache to store read result of source pixel
angle::DepthStencil depthStencilData; angle::DepthStencil depthStencilData;
auto sourcePixelReadData = reinterpret_cast<uint8_t *>(&depthStencilData); auto sourcePixelReadData = reinterpret_cast<uint8_t *>(&depthStencilData);
ASSERT(textureAngleFormat.pixelBytes <= sizeof(depthStencilData)); ASSERT(srcAngleFormat.pixelBytes <= sizeof(depthStencilData));
for (NSUInteger d = 0; d < regionSize.depth; ++d)
{
for (NSUInteger r = 0; r < regionSize.height; ++r) for (NSUInteger r = 0; r < regionSize.height; ++r)
{ {
for (NSUInteger c = 0; c < regionSize.width; ++c) for (NSUInteger c = 0; c < regionSize.width; ++c)
{ {
const uint8_t *sourcePixelData = const uint8_t *sourcePixelData =
data + r * bytesPerRow + c * textureAngleFormat.pixelBytes; psrc + d * src2DImageSize + r * srcRowPitch + c * srcAngleFormat.pixelBytes;
uint8_t *destPixelData = uint8_t *destPixelData = pdst + d * dest2DImageSize + r * destRowPitch +
pdst + r * stagingBufferRowPitch + c * stagingAngleFormat.pixelBytes; c * dstAngleFormat.pixelBytes;
textureAngleFormat.pixelReadFunction(sourcePixelData, sourcePixelReadData); srcAngleFormat.pixelReadFunction(sourcePixelData, sourcePixelReadData);
stagingAngleFormat.pixelWriteFunction(sourcePixelReadData, destPixelData); pixelWriteFunction(sourcePixelReadData, destPixelData);
} }
} }
} }
}
}
angle::Result CopyDepthStencilTextureContentsToStagingBuffer(
ContextMtl *contextMtl,
const angle::Format &textureAngleFormat,
const angle::Format &stagingAngleFormat,
rx::PixelWriteFunction pixelWriteFunctionOverride,
const MTLSize &regionSize,
const uint8_t *data,
size_t bytesPerRow,
size_t bytesPer2DImage,
size_t *bufferRowPitchOut,
size_t *buffer2DImageSizeOut,
mtl::BufferRef *bufferOut)
{
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);
ConvertDepthStencilData(regionSize, textureAngleFormat, bytesPerRow, bytesPer2DImage, data,
stagingAngleFormat, pixelWriteFunctionOverride, stagingBufferRowPitch,
stagingBuffer2DImageSize, pdst);
stagingBuffer->unmap(contextMtl); stagingBuffer->unmap(contextMtl);
...@@ -144,14 +235,16 @@ angle::Result CopyTextureContentsToStagingBuffer(ContextMtl *contextMtl, ...@@ -144,14 +235,16 @@ angle::Result CopyTextureContentsToStagingBuffer(ContextMtl *contextMtl,
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result UploadTextureContentsWithStagingBuffer(ContextMtl *contextMtl, angle::Result UploadDepthStencilTextureContentsWithStagingBuffer(
const mtl::TextureRef &texture, ContextMtl *contextMtl,
const angle::Format &textureAngleFormat, const angle::Format &textureAngleFormat,
MTLRegion region, MTLRegion region,
uint32_t mipmapLevel, uint32_t mipmapLevel,
uint32_t slice, uint32_t slice,
const uint8_t *data, const uint8_t *data,
size_t bytesPerRow) size_t bytesPerRow,
size_t bytesPer2DImage,
const mtl::TextureRef &texture)
{ {
ASSERT(texture && texture->valid()); ASSERT(texture && texture->valid());
...@@ -166,9 +259,10 @@ angle::Result UploadTextureContentsWithStagingBuffer(ContextMtl *contextMtl, ...@@ -166,9 +259,10 @@ angle::Result UploadTextureContentsWithStagingBuffer(ContextMtl *contextMtl,
size_t stagingBufferRowPitch; size_t stagingBufferRowPitch;
size_t stagingBuffer2DImageSize; size_t stagingBuffer2DImageSize;
mtl::BufferRef stagingBuffer; mtl::BufferRef stagingBuffer;
ANGLE_TRY(CopyTextureContentsToStagingBuffer( ANGLE_TRY(CopyDepthStencilTextureContentsToStagingBuffer(
contextMtl, textureAngleFormat, textureAngleFormat, region.size, data, bytesPerRow, contextMtl, textureAngleFormat, textureAngleFormat, textureAngleFormat.pixelWriteFunction,
&stagingBufferRowPitch, &stagingBuffer2DImageSize, &stagingBuffer)); region.size, data, bytesPerRow, bytesPer2DImage, &stagingBufferRowPitch,
&stagingBuffer2DImageSize, &stagingBuffer));
// Copy staging buffer to texture. // Copy staging buffer to texture.
mtl::BlitCommandEncoder *encoder = contextMtl->getBlitCommandEncoder(); mtl::BlitCommandEncoder *encoder = contextMtl->getBlitCommandEncoder();
...@@ -182,13 +276,14 @@ angle::Result UploadTextureContentsWithStagingBuffer(ContextMtl *contextMtl, ...@@ -182,13 +276,14 @@ angle::Result UploadTextureContentsWithStagingBuffer(ContextMtl *contextMtl,
// Packed depth stencil upload using staging buffer // Packed depth stencil upload using staging buffer
angle::Result UploadPackedDepthStencilTextureContentsWithStagingBuffer( angle::Result UploadPackedDepthStencilTextureContentsWithStagingBuffer(
ContextMtl *contextMtl, ContextMtl *contextMtl,
const mtl::TextureRef &texture,
const angle::Format &textureAngleFormat, const angle::Format &textureAngleFormat,
MTLRegion region, MTLRegion region,
uint32_t mipmapLevel, uint32_t mipmapLevel,
uint32_t slice, uint32_t slice,
const uint8_t *data, const uint8_t *data,
size_t bytesPerRow) size_t bytesPerRow,
size_t bytesPer2DImage,
const mtl::TextureRef &texture)
{ {
ASSERT(texture && texture->valid()); ASSERT(texture && texture->valid());
...@@ -199,11 +294,17 @@ angle::Result UploadPackedDepthStencilTextureContentsWithStagingBuffer( ...@@ -199,11 +294,17 @@ angle::Result UploadPackedDepthStencilTextureContentsWithStagingBuffer(
// We have to split the depth & stencil data into 2 buffers. // We have to split the depth & stencil data into 2 buffers.
angle::FormatID stagingDepthBufferFormatId; angle::FormatID stagingDepthBufferFormatId;
angle::FormatID stagingStencilBufferFormatId; angle::FormatID stagingStencilBufferFormatId;
// Custom depth write function. We cannot use those in imageformats.cpp since Metal has some
// special cases.
rx::PixelWriteFunction stagingDepthBufferWriteFunctionOverride = nullptr;
switch (textureAngleFormat.id) switch (textureAngleFormat.id)
{ {
case angle::FormatID::D24_UNORM_S8_UINT: case angle::FormatID::D24_UNORM_S8_UINT:
// D24_UNORM_X8_UINT writes depth data to high 24 bits. But Metal expects depth data to
// be in low 24 bits.
stagingDepthBufferFormatId = angle::FormatID::D24_UNORM_X8_UINT; stagingDepthBufferFormatId = angle::FormatID::D24_UNORM_X8_UINT;
stagingDepthBufferWriteFunctionOverride = WriteDepthStencilToDepth24;
stagingStencilBufferFormatId = angle::FormatID::S8_UINT; stagingStencilBufferFormatId = angle::FormatID::S8_UINT;
break; break;
case angle::FormatID::D32_FLOAT_S8X24_UINT: case angle::FormatID::D32_FLOAT_S8X24_UINT:
...@@ -222,13 +323,17 @@ angle::Result UploadPackedDepthStencilTextureContentsWithStagingBuffer( ...@@ -222,13 +323,17 @@ angle::Result UploadPackedDepthStencilTextureContentsWithStagingBuffer(
size_t stagingDepthBuffer2DImageSize, stagingStencilBuffer2DImageSize; size_t stagingDepthBuffer2DImageSize, stagingStencilBuffer2DImageSize;
mtl::BufferRef stagingDepthbuffer, stagingStencilBuffer; mtl::BufferRef stagingDepthbuffer, stagingStencilBuffer;
ANGLE_TRY(CopyTextureContentsToStagingBuffer( // Copy depth data to staging depth buffer
contextMtl, textureAngleFormat, angleStagingDepthFormat, region.size, data, bytesPerRow, ANGLE_TRY(CopyDepthStencilTextureContentsToStagingBuffer(
contextMtl, textureAngleFormat, angleStagingDepthFormat,
stagingDepthBufferWriteFunctionOverride, region.size, data, bytesPerRow, bytesPer2DImage,
&stagingDepthBufferRowPitch, &stagingDepthBuffer2DImageSize, &stagingDepthbuffer)); &stagingDepthBufferRowPitch, &stagingDepthBuffer2DImageSize, &stagingDepthbuffer));
ANGLE_TRY(CopyTextureContentsToStagingBuffer( // Copy stencil data to staging stencil buffer
contextMtl, textureAngleFormat, angleStagingStencilFormat, region.size, data, bytesPerRow, ANGLE_TRY(CopyDepthStencilTextureContentsToStagingBuffer(
&stagingStencilBufferRowPitch, &stagingStencilBuffer2DImageSize, &stagingStencilBuffer)); contextMtl, textureAngleFormat, angleStagingStencilFormat, nullptr, region.size, data,
bytesPerRow, bytesPer2DImage, &stagingStencilBufferRowPitch,
&stagingStencilBuffer2DImageSize, &stagingStencilBuffer));
mtl::BlitCommandEncoder *encoder = contextMtl->getBlitCommandEncoder(); mtl::BlitCommandEncoder *encoder = contextMtl->getBlitCommandEncoder();
...@@ -243,13 +348,14 @@ angle::Result UploadPackedDepthStencilTextureContentsWithStagingBuffer( ...@@ -243,13 +348,14 @@ angle::Result UploadPackedDepthStencilTextureContentsWithStagingBuffer(
} }
angle::Result UploadTextureContents(const gl::Context *context, angle::Result UploadTextureContents(const gl::Context *context,
const mtl::TextureRef &texture,
const angle::Format &textureAngleFormat, const angle::Format &textureAngleFormat,
const MTLRegion &region, const MTLRegion &region,
uint32_t mipmapLevel, uint32_t mipmapLevel,
uint32_t slice, uint32_t slice,
const uint8_t *data, const uint8_t *data,
size_t bytesPerRow) size_t bytesPerRow,
size_t bytesPer2DImage,
const mtl::TextureRef &texture)
{ {
ASSERT(texture && texture->valid()); ASSERT(texture && texture->valid());
ContextMtl *contextMtl = mtl::GetImpl(context); ContextMtl *contextMtl = mtl::GetImpl(context);
...@@ -257,23 +363,26 @@ angle::Result UploadTextureContents(const gl::Context *context, ...@@ -257,23 +363,26 @@ angle::Result UploadTextureContents(const gl::Context *context,
if (texture->isCPUAccessible()) if (texture->isCPUAccessible())
{ {
// If texture is CPU accessible, just call replaceRegion() directly. // If texture is CPU accessible, just call replaceRegion() directly.
texture->replaceRegion(contextMtl, region, mipmapLevel, slice, data, bytesPerRow); texture->replaceRegion(contextMtl, region, mipmapLevel, slice, data, bytesPerRow,
bytesPer2DImage);
return angle::Result::Continue; return angle::Result::Continue;
} }
ASSERT(textureAngleFormat.depthBits || textureAngleFormat.stencilBits);
// Texture is not CPU accessible, we need to use staging buffer // Texture is not CPU accessible, we need to use staging buffer
if (textureAngleFormat.depthBits && textureAngleFormat.stencilBits) if (textureAngleFormat.depthBits && textureAngleFormat.stencilBits)
{ {
ANGLE_TRY(UploadPackedDepthStencilTextureContentsWithStagingBuffer( ANGLE_TRY(UploadPackedDepthStencilTextureContentsWithStagingBuffer(
contextMtl, texture, textureAngleFormat, region, mipmapLevel, slice, data, contextMtl, textureAngleFormat, region, mipmapLevel, slice, data, bytesPerRow,
bytesPerRow)); bytesPer2DImage, texture));
} }
else else
{ {
ANGLE_TRY(UploadTextureContentsWithStagingBuffer(contextMtl, texture, textureAngleFormat, ANGLE_TRY(UploadDepthStencilTextureContentsWithStagingBuffer(
region, mipmapLevel, slice, data, contextMtl, textureAngleFormat, region, mipmapLevel, slice, data, bytesPerRow,
bytesPerRow)); bytesPer2DImage, texture));
} }
return angle::Result::Continue; return angle::Result::Continue;
...@@ -293,24 +402,40 @@ void TextureMtl::onDestroy(const gl::Context *context) ...@@ -293,24 +402,40 @@ void TextureMtl::onDestroy(const gl::Context *context)
void TextureMtl::releaseTexture(bool releaseImages) void TextureMtl::releaseTexture(bool releaseImages)
{ {
mFormat = mtl::Format(); releaseTexture(releaseImages, false);
}
mNativeTexture = nullptr; void TextureMtl::releaseTexture(bool releaseImages, bool releaseTextureObjectsOnly)
mMetalSamplerState = nil; {
for (RenderTargetMtl &rt : mLayeredRenderTargets) if (releaseImages)
{ {
rt.reset(); mTexImageDefs.clear();
} }
else if (mNativeTexture)
if (releaseImages)
{ {
mTexImages.clear(); // Release native texture but keep its old per face per mipmap level image views.
retainImageDefinitions();
} }
mLayeredTextureViews.clear(); mNativeTexture = nullptr;
// Clear render target cache for each texture's image. We don't erase them because they
// might still be referenced by a framebuffer.
for (auto &sliceRenderTargets : mPerLayerRenderTargets)
{
for (RenderTargetMtl &mipRenderTarget : sliceRenderTargets.second)
{
mipRenderTarget.reset();
}
}
if (!releaseTextureObjectsOnly)
{
mMetalSamplerState = nil;
mFormat = mtl::Format();
mIsPow2 = false; mIsPow2 = false;
}
} }
angle::Result TextureMtl::ensureTextureCreated(const gl::Context *context) angle::Result TextureMtl::ensureTextureCreated(const gl::Context *context)
...@@ -323,36 +448,47 @@ angle::Result TextureMtl::ensureTextureCreated(const gl::Context *context) ...@@ -323,36 +448,47 @@ angle::Result TextureMtl::ensureTextureCreated(const gl::Context *context)
ContextMtl *contextMtl = mtl::GetImpl(context); ContextMtl *contextMtl = mtl::GetImpl(context);
// Create actual texture object: // Create actual texture object:
int layers = 0; const GLuint mips = mState.getMipmapMaxLevel() - mState.getEffectiveBaseLevel() + 1;
const GLuint mips = mState.getMipmapMaxLevel() + 1; gl::ImageDesc desc = mState.getBaseLevelDesc();
const gl::ImageDesc &desc = mState.getBaseLevelDesc(); ANGLE_MTL_CHECK(contextMtl, desc.format.valid(), GL_INVALID_OPERATION);
angle::FormatID angleFormatId =
angle::Format::InternalFormatToID(desc.format.info->sizedInternalFormat);
mFormat = contextMtl->getPixelFormat(angleFormatId);
mIsPow2 = gl::isPow2(desc.size.width) && gl::isPow2(desc.size.height); return createNativeTexture(context, mState.getType(), mips, desc.size);
}
switch (mState.getType()) angle::Result TextureMtl::createNativeTexture(const gl::Context *context,
gl::TextureType type,
GLuint mips,
const gl::Extents &size)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
// Create actual texture object:
mIsPow2 = gl::isPow2(size.width) && gl::isPow2(size.height) && gl::isPow2(size.depth);
mSlices = 1;
int numCubeFaces = 1;
switch (type)
{ {
case gl::TextureType::_2D: case gl::TextureType::_2D:
layers = 1;
ANGLE_TRY(mtl::Texture::Make2DTexture( ANGLE_TRY(mtl::Texture::Make2DTexture(
contextMtl, mFormat, desc.size.width, desc.size.height, mips, false, contextMtl, mFormat, size.width, size.height, mips,
mFormat.hasDepthAndStencilBits(), &mNativeTexture)); /** renderTargetOnly */ false,
mLayeredRenderTargets.resize(1); /** allowFormatView */ mFormat.hasDepthAndStencilBits(), &mNativeTexture));
mLayeredRenderTargets[0].set(mNativeTexture, 0, 0, mFormat);
mLayeredTextureViews.resize(1);
mLayeredTextureViews[0] = mNativeTexture;
break; break;
case gl::TextureType::CubeMap: case gl::TextureType::CubeMap:
layers = 6; mSlices = numCubeFaces = 6;
ANGLE_TRY(mtl::Texture::MakeCubeTexture(contextMtl, mFormat, desc.size.width, mips, ANGLE_TRY(mtl::Texture::MakeCubeTexture(
false, mFormat.hasDepthAndStencilBits(), contextMtl, mFormat, size.width, mips,
&mNativeTexture)); /** renderTargetOnly */ false,
mLayeredRenderTargets.resize(gl::kCubeFaceCount); /** allowFormatView */ mFormat.hasDepthAndStencilBits(), &mNativeTexture));
mLayeredTextureViews.resize(gl::kCubeFaceCount); break;
for (uint32_t f = 0; f < gl::kCubeFaceCount; ++f) case gl::TextureType::_3D:
{ ANGLE_TRY(mtl::Texture::Make3DTexture(
mLayeredTextureViews[f] = mNativeTexture->createCubeFaceView(f); contextMtl, mFormat, size.width, size.height, size.depth, mips,
mLayeredRenderTargets[f].set(mLayeredTextureViews[f], 0, 0, mFormat); /** renderTargetOnly */ false,
} /** allowFormatView */ mFormat.hasDepthAndStencilBits(), &mNativeTexture));
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
...@@ -362,11 +498,12 @@ angle::Result TextureMtl::ensureTextureCreated(const gl::Context *context) ...@@ -362,11 +498,12 @@ angle::Result TextureMtl::ensureTextureCreated(const gl::Context *context)
// Transfer data from images to actual texture object // Transfer data from images to actual texture object
mtl::BlitCommandEncoder *encoder = nullptr; mtl::BlitCommandEncoder *encoder = nullptr;
for (int layer = 0; layer < layers; ++layer) for (int face = 0; face < numCubeFaces; ++face)
{ {
for (GLuint mip = 0; mip < mips; ++mip) for (GLuint mip = 0; mip < mips; ++mip)
{ {
mtl::TextureRef &imageToTransfer = mTexImages[layer][mip]; GLuint imageMipLevel = mip + mState.getEffectiveBaseLevel();
mtl::TextureRef &imageToTransfer = mTexImageDefs[face][imageMipLevel].image;
// Only transfer if this mip & slice image has been defined and in correct size & // Only transfer if this mip & slice image has been defined and in correct size &
// format. // format.
...@@ -374,23 +511,15 @@ angle::Result TextureMtl::ensureTextureCreated(const gl::Context *context) ...@@ -374,23 +511,15 @@ angle::Result TextureMtl::ensureTextureCreated(const gl::Context *context)
if (imageToTransfer && imageToTransfer->size() == actualMipSize && if (imageToTransfer && imageToTransfer->size() == actualMipSize &&
imageToTransfer->pixelFormat() == mNativeTexture->pixelFormat()) imageToTransfer->pixelFormat() == mNativeTexture->pixelFormat())
{ {
MTLSize mtlSize =
MTLSizeMake(actualMipSize.width, actualMipSize.height, actualMipSize.depth);
MTLOrigin mtlOrigin = MTLOriginMake(0, 0, 0);
if (!encoder) if (!encoder)
{ {
encoder = contextMtl->getBlitCommandEncoder(); encoder = contextMtl->getBlitCommandEncoder();
} }
encoder->copyTexture(imageToTransfer, 0, 0, mtlOrigin, mtlSize, mNativeTexture, encoder->copyTexture(imageToTransfer, 0, 0, mNativeTexture, face, mip,
layer, mip, mtlOrigin); imageToTransfer->arrayLength(), 1);
} }
imageToTransfer = nullptr; imageToTransfer = nullptr;
// Make this image the actual texture object's view at this mip and slice.
// So that in future, glTexSubImage* will update the actual texture
// directly.
mTexImages[layer][mip] = mNativeTexture->createSliceMipView(layer, mip);
} }
} }
...@@ -436,7 +565,7 @@ angle::Result TextureMtl::ensureSamplerStateCreated(const gl::Context *context) ...@@ -436,7 +565,7 @@ angle::Result TextureMtl::ensureSamplerStateCreated(const gl::Context *context)
angle::Result TextureMtl::ensureImageCreated(const gl::Context *context, angle::Result TextureMtl::ensureImageCreated(const gl::Context *context,
const gl::ImageIndex &index) const gl::ImageIndex &index)
{ {
mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()]; mtl::TextureRef &image = getImage(index);
if (!image) if (!image)
{ {
// Image at this level hasn't been defined yet. We need to define it: // Image at this level hasn't been defined yet. We need to define it:
...@@ -446,6 +575,132 @@ angle::Result TextureMtl::ensureImageCreated(const gl::Context *context, ...@@ -446,6 +575,132 @@ angle::Result TextureMtl::ensureImageCreated(const gl::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
mtl::TextureRef TextureMtl::createImageViewFromNativeTexture(GLuint cubeFaceOrZero,
GLuint nativeLevel)
{
mtl::TextureRef image;
if (mNativeTexture->textureType() == MTLTextureTypeCube)
{
// Cube texture's image is per face.
image = mNativeTexture->createSliceMipView(cubeFaceOrZero, nativeLevel);
}
else
{
image = mNativeTexture->createMipView(nativeLevel);
}
return image;
}
void TextureMtl::retainImageDefinitions()
{
if (!mNativeTexture)
{
return;
}
const GLuint mips = mNativeTexture->mipmapLevels();
int numCubeFaces = 1;
switch (mState.getType())
{
case gl::TextureType::CubeMap:
numCubeFaces = 6;
break;
default:
break;
}
// Create image view per cube face, per mip level
for (int face = 0; face < numCubeFaces; ++face)
{
for (GLuint mip = 0; mip < mips; ++mip)
{
GLuint imageMipLevel = mip + mState.getEffectiveBaseLevel();
ImageDefinitionMtl &imageDef = mTexImageDefs[face][imageMipLevel];
if (imageDef.image)
{
continue;
}
imageDef.image = createImageViewFromNativeTexture(face, mip);
imageDef.formatID = mFormat.intendedFormatId;
}
}
}
bool TextureMtl::isIndexWithinMinMaxLevels(const gl::ImageIndex &imageIndex) const
{
return imageIndex.getLevelIndex() >= static_cast<GLint>(mState.getEffectiveBaseLevel()) &&
imageIndex.getLevelIndex() <= static_cast<GLint>(mState.getEffectiveMaxLevel());
}
int TextureMtl::getNativeLevel(const gl::ImageIndex &imageIndex) const
{
int baseLevel = mState.getEffectiveBaseLevel();
int adjustedLevel = imageIndex.getLevelIndex() - baseLevel;
return adjustedLevel;
}
mtl::TextureRef &TextureMtl::getImage(const gl::ImageIndex &imageIndex)
{
return getImageDefinition(imageIndex).image;
}
ImageDefinitionMtl &TextureMtl::getImageDefinition(const gl::ImageIndex &imageIndex)
{
GLuint cubeFaceOrZero = GetImageCubeFaceIndexOrZero(imageIndex);
ImageDefinitionMtl &imageDef = mTexImageDefs[cubeFaceOrZero][imageIndex.getLevelIndex()];
if (!imageDef.image && mNativeTexture)
{
// If native texture is already created, and the image at this index is not available,
// then create a view of native texture at this index, so that modifications of the image
// are reflected back to native texture's respective index.
if (!isIndexWithinMinMaxLevels(imageIndex))
{
// Image below base level is skipped.
return imageDef;
}
uint32_t nativeLevel = getNativeLevel(imageIndex);
if (nativeLevel >= mNativeTexture->mipmapLevels())
{
// Image outside native texture's mip levels is skipped.
return imageDef;
}
imageDef.image = createImageViewFromNativeTexture(cubeFaceOrZero, nativeLevel);
imageDef.formatID = mFormat.intendedFormatId;
}
return imageDef;
}
RenderTargetMtl &TextureMtl::getRenderTarget(const gl::ImageIndex &imageIndex)
{
ASSERT(imageIndex.getType() == gl::TextureType::_2D ||
imageIndex.getType() == gl::TextureType::Rectangle ||
imageIndex.getType() == gl::TextureType::_2DMultisample || imageIndex.hasLayer());
GLuint layer = GetImageLayerIndex(imageIndex);
RenderTargetMtl &rtt = mPerLayerRenderTargets[layer][imageIndex.getLevelIndex()];
if (!rtt.getTexture())
{
// Lazy initialization of render target:
mtl::TextureRef &image = getImage(imageIndex);
if (image)
{
if (imageIndex.getType() == gl::TextureType::CubeMap)
{
// Cube map is special, the image is already the view of its layer
rtt.set(image, 0, 0, mFormat);
}
else
{
rtt.set(image, 0, layer, mFormat);
}
}
}
return rtt;
}
angle::Result TextureMtl::setImage(const gl::Context *context, angle::Result TextureMtl::setImage(const gl::Context *context,
const gl::ImageIndex &index, const gl::ImageIndex &index,
GLenum internalFormat, GLenum internalFormat,
...@@ -456,9 +711,10 @@ angle::Result TextureMtl::setImage(const gl::Context *context, ...@@ -456,9 +711,10 @@ angle::Result TextureMtl::setImage(const gl::Context *context,
gl::Buffer *unpackBuffer, gl::Buffer *unpackBuffer,
const uint8_t *pixels) const uint8_t *pixels)
{ {
const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat, type); const gl::InternalFormat &dstFormatInfo = gl::GetInternalFormatInfo(internalFormat, type);
return setImageImpl(context, index, formatInfo, size, type, unpack, pixels); return setImageImpl(context, index, dstFormatInfo, size, format, type, unpack, unpackBuffer,
pixels);
} }
angle::Result TextureMtl::setSubImage(const gl::Context *context, angle::Result TextureMtl::setSubImage(const gl::Context *context,
...@@ -472,7 +728,7 @@ angle::Result TextureMtl::setSubImage(const gl::Context *context, ...@@ -472,7 +728,7 @@ angle::Result TextureMtl::setSubImage(const gl::Context *context,
{ {
const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format, type); const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format, type);
return setSubImageImpl(context, index, area, formatInfo, type, unpack, pixels); return setSubImageImpl(context, index, area, formatInfo, type, unpack, unpackBuffer, pixels);
} }
angle::Result TextureMtl::setCompressedImage(const gl::Context *context, angle::Result TextureMtl::setCompressedImage(const gl::Context *context,
...@@ -484,8 +740,11 @@ angle::Result TextureMtl::setCompressedImage(const gl::Context *context, ...@@ -484,8 +740,11 @@ angle::Result TextureMtl::setCompressedImage(const gl::Context *context,
const uint8_t *pixels) const uint8_t *pixels)
{ {
const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalFormat); const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalFormat);
const gl::State &glState = context->getState();
gl::Buffer *unpackBuffer = glState.getTargetBuffer(gl::BufferBinding::PixelUnpack);
return setImageImpl(context, index, formatInfo, size, GL_UNSIGNED_BYTE, unpack, pixels); return setImageImpl(context, index, formatInfo, size, internalFormat, GL_UNSIGNED_BYTE, unpack,
unpackBuffer, pixels);
} }
angle::Result TextureMtl::setCompressedSubImage(const gl::Context *context, angle::Result TextureMtl::setCompressedSubImage(const gl::Context *context,
...@@ -499,7 +758,11 @@ angle::Result TextureMtl::setCompressedSubImage(const gl::Context *context, ...@@ -499,7 +758,11 @@ angle::Result TextureMtl::setCompressedSubImage(const gl::Context *context,
const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format, GL_UNSIGNED_BYTE); const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format, GL_UNSIGNED_BYTE);
return setSubImageImpl(context, index, area, formatInfo, GL_UNSIGNED_BYTE, unpack, pixels); const gl::State &glState = context->getState();
gl::Buffer *unpackBuffer = glState.getTargetBuffer(gl::BufferBinding::PixelUnpack);
return setSubImageImpl(context, index, area, formatInfo, GL_UNSIGNED_BYTE, unpack, unpackBuffer,
pixels);
} }
angle::Result TextureMtl::copyImage(const gl::Context *context, angle::Result TextureMtl::copyImage(const gl::Context *context,
...@@ -662,10 +925,9 @@ angle::Result TextureMtl::generateMipmap(const gl::Context *context) ...@@ -662,10 +925,9 @@ angle::Result TextureMtl::generateMipmap(const gl::Context *context)
return angle::Result::Continue; return angle::Result::Continue;
} }
const gl::TextureCapsMap &textureCapsMap = contextMtl->getNativeTextureCaps(); const mtl::FormatCaps &caps = mFormat.getCaps();
const gl::TextureCaps &textureCaps = textureCapsMap.get(mFormat.intendedFormatId);
if (textureCaps.filterable && textureCaps.renderbuffer) if (caps.filterable && caps.colorRenderable)
{ {
mtl::BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder(); mtl::BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder();
blitEncoder->generateMipmapsForTexture(mNativeTexture); blitEncoder->generateMipmapsForTexture(mNativeTexture);
...@@ -681,40 +943,42 @@ angle::Result TextureMtl::generateMipmap(const gl::Context *context) ...@@ -681,40 +943,42 @@ angle::Result TextureMtl::generateMipmap(const gl::Context *context)
angle::Result TextureMtl::generateMipmapCPU(const gl::Context *context) angle::Result TextureMtl::generateMipmapCPU(const gl::Context *context)
{ {
ASSERT(mNativeTexture && mNativeTexture->valid()); ASSERT(mNativeTexture && mNativeTexture->valid());
ASSERT(mLayeredTextureViews.size() <= std::numeric_limits<uint32_t>::max());
uint32_t layers = static_cast<uint32_t>(mLayeredTextureViews.size());
ContextMtl *contextMtl = mtl::GetImpl(context); ContextMtl *contextMtl = mtl::GetImpl(context);
const angle::Format &angleFormat = mFormat.actualAngleFormat(); const angle::Format &angleFormat = mFormat.actualAngleFormat();
// This format must have mip generation function. // This format must have mip generation function.
ANGLE_MTL_TRY(contextMtl, angleFormat.mipGenerationFunction); ANGLE_MTL_TRY(contextMtl, angleFormat.mipGenerationFunction);
// NOTE(hqle): Support base level of ES 3.0. for (uint32_t slice = 0; slice < mSlices; ++slice)
for (uint32_t layer = 0; layer < layers; ++layer)
{ {
int maxMipLevel = static_cast<int>(mNativeTexture->mipmapLevels()) - 1; int maxMipLevel = static_cast<int>(mNativeTexture->mipmapLevels()) - 1;
int firstLevel = 0; int firstLevel = 0;
uint32_t prevLevelWidth = mNativeTexture->width(); uint32_t prevLevelWidth = mNativeTexture->width();
uint32_t prevLevelHeight = mNativeTexture->height(); uint32_t prevLevelHeight = mNativeTexture->height();
uint32_t prevLevelDepth = mNativeTexture->depth();
size_t prevLevelRowPitch = angleFormat.pixelBytes * prevLevelWidth; size_t prevLevelRowPitch = angleFormat.pixelBytes * prevLevelWidth;
size_t prevLevelDepthPitch = prevLevelRowPitch * prevLevelHeight;
std::unique_ptr<uint8_t[]> prevLevelData(new (std::nothrow) std::unique_ptr<uint8_t[]> prevLevelData(new (std::nothrow)
uint8_t[prevLevelRowPitch * prevLevelHeight]); uint8_t[prevLevelDepthPitch * prevLevelDepth]);
ANGLE_CHECK_GL_ALLOC(contextMtl, prevLevelData); ANGLE_CHECK_GL_ALLOC(contextMtl, prevLevelData);
std::unique_ptr<uint8_t[]> dstLevelData; std::unique_ptr<uint8_t[]> dstLevelData;
// Download base level data // Download base level data
mLayeredTextureViews[layer]->getBytes( mNativeTexture->getBytes(
contextMtl, prevLevelRowPitch, MTLRegionMake2D(0, 0, prevLevelWidth, prevLevelHeight), contextMtl, prevLevelRowPitch, prevLevelDepthPitch,
firstLevel, prevLevelData.get()); MTLRegionMake3D(0, 0, 0, prevLevelWidth, prevLevelHeight, prevLevelDepth), firstLevel,
slice, prevLevelData.get());
for (int mip = firstLevel + 1; mip <= maxMipLevel; ++mip) for (int mip = firstLevel + 1; mip <= maxMipLevel; ++mip)
{ {
uint32_t dstWidth = mNativeTexture->width(mip); uint32_t dstWidth = mNativeTexture->width(mip);
uint32_t dstHeight = mNativeTexture->height(mip); uint32_t dstHeight = mNativeTexture->height(mip);
uint32_t dstDepth = mNativeTexture->depth(mip);
size_t dstRowPitch = angleFormat.pixelBytes * dstWidth; size_t dstRowPitch = angleFormat.pixelBytes * dstWidth;
size_t dstDataSize = dstRowPitch * dstHeight; size_t dstDepthPitch = dstRowPitch * dstHeight;
size_t dstDataSize = dstDepthPitch * dstDepth;
if (!dstLevelData) if (!dstLevelData)
{ {
// Allocate once and reuse the buffer // Allocate once and reuse the buffer
...@@ -723,18 +987,20 @@ angle::Result TextureMtl::generateMipmapCPU(const gl::Context *context) ...@@ -723,18 +987,20 @@ angle::Result TextureMtl::generateMipmapCPU(const gl::Context *context)
} }
// Generate mip level // Generate mip level
angleFormat.mipGenerationFunction(prevLevelWidth, prevLevelHeight, 1, angleFormat.mipGenerationFunction(
prevLevelData.get(), prevLevelRowPitch, 0, prevLevelWidth, prevLevelHeight, 1, prevLevelData.get(), prevLevelRowPitch,
dstLevelData.get(), dstRowPitch, 0); prevLevelDepthPitch, dstLevelData.get(), dstRowPitch, dstDepthPitch);
// Upload to texture // Upload to texture
ANGLE_TRY(UploadTextureContents(context, mNativeTexture, angleFormat, ANGLE_TRY(UploadTextureContents(
MTLRegionMake2D(0, 0, dstWidth, dstHeight), mip, layer, context, angleFormat, MTLRegionMake3D(0, 0, 0, dstWidth, dstHeight, dstDepth), mip,
dstLevelData.get(), dstRowPitch)); slice, dstLevelData.get(), dstRowPitch, dstDepthPitch, mNativeTexture));
prevLevelWidth = dstWidth; prevLevelWidth = dstWidth;
prevLevelHeight = dstHeight; prevLevelHeight = dstHeight;
prevLevelDepth = dstDepth;
prevLevelRowPitch = dstRowPitch; prevLevelRowPitch = dstRowPitch;
prevLevelDepthPitch = dstDepthPitch;
std::swap(prevLevelData, dstLevelData); std::swap(prevLevelData, dstLevelData);
} // for mip level } // for mip level
...@@ -762,16 +1028,6 @@ angle::Result TextureMtl::bindTexImage(const gl::Context *context, egl::Surface ...@@ -762,16 +1028,6 @@ angle::Result TextureMtl::bindTexImage(const gl::Context *context, egl::Surface
mIsPow2 = gl::isPow2(size.width) && gl::isPow2(size.height) && gl::isPow2(size.depth); mIsPow2 = gl::isPow2(size.width) && gl::isPow2(size.height) && gl::isPow2(size.depth);
ANGLE_TRY(ensureSamplerStateCreated(context)); ANGLE_TRY(ensureSamplerStateCreated(context));
ASSERT(mState.getType() == gl::TextureType::_2D);
mLayeredRenderTargets.resize(1);
mLayeredRenderTargets[0].set(mNativeTexture, 0, 0, mFormat);
mLayeredTextureViews.resize(1);
mLayeredTextureViews[0] = mNativeTexture;
// Create image view for glTexSubImage* and glCopySubTexture*
ASSERT(mNativeTexture->mipmapLevels() == 1);
mTexImages[0][0] = mNativeTexture;
// Tell context to rebind textures // Tell context to rebind textures
ContextMtl *contextMtl = mtl::GetImpl(context); ContextMtl *contextMtl = mtl::GetImpl(context);
contextMtl->invalidateCurrentTextures(); contextMtl->invalidateCurrentTextures();
...@@ -792,24 +1048,11 @@ angle::Result TextureMtl::getAttachmentRenderTarget(const gl::Context *context, ...@@ -792,24 +1048,11 @@ angle::Result TextureMtl::getAttachmentRenderTarget(const gl::Context *context,
FramebufferAttachmentRenderTarget **rtOut) FramebufferAttachmentRenderTarget **rtOut)
{ {
ANGLE_TRY(ensureTextureCreated(context)); ANGLE_TRY(ensureTextureCreated(context));
// NOTE(hqle): Support MSAA.
// Non-zero mip level attachments are an ES 3.0 feature.
ASSERT(imageIndex.getLevelIndex() == 0);
ContextMtl *contextMtl = mtl::GetImpl(context); ContextMtl *contextMtl = mtl::GetImpl(context);
ANGLE_MTL_TRY(contextMtl, mNativeTexture); ANGLE_MTL_TRY(contextMtl, mNativeTexture);
switch (imageIndex.getType()) *rtOut = &getRenderTarget(imageIndex);
{
case gl::TextureType::_2D:
*rtOut = &mLayeredRenderTargets[0];
break;
case gl::TextureType::CubeMap:
*rtOut = &mLayeredRenderTargets[imageIndex.cubeMapFaceIndex()];
break;
default:
UNREACHABLE();
}
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -853,22 +1096,22 @@ angle::Result TextureMtl::redefineImage(const gl::Context *context, ...@@ -853,22 +1096,22 @@ angle::Result TextureMtl::redefineImage(const gl::Context *context,
const mtl::Format &mtlFormat, const mtl::Format &mtlFormat,
const gl::Extents &size) const gl::Extents &size)
{ {
if (mNativeTexture) bool imageWithinLevelRange = false;
{
if (mNativeTexture->valid()) if (isIndexWithinMinMaxLevels(index) && mNativeTexture && mNativeTexture->valid())
{ {
imageWithinLevelRange = true;
int nativeLevel = getNativeLevel(index);
// Calculate the expected size for the index we are defining. If the size is different // Calculate the expected size for the index we are defining. If the size is different
// from the given size, or the format is different, we are redefining the image so we // from the given size, or the format is different, we are redefining the image so we
// must release it. // must release it.
bool typeChanged = bool typeChanged = mNativeTexture->textureType() != mtl::GetTextureType(index.getType());
mNativeTexture->textureType() != mtl::GetTextureType(index.getType()); if (mFormat != mtlFormat || size != mNativeTexture->size(nativeLevel) || typeChanged)
if (mFormat != mtlFormat || size != mNativeTexture->size(index) || typeChanged)
{ {
// Keep other images data if texture type hasn't been changed. // Keep other images data if texture type hasn't been changed.
releaseTexture(typeChanged); releaseTexture(typeChanged);
} }
} }
}
// Early-out on empty textures, don't create a zero-sized storage. // Early-out on empty textures, don't create a zero-sized storage.
if (size.empty()) if (size.empty())
...@@ -879,24 +1122,34 @@ angle::Result TextureMtl::redefineImage(const gl::Context *context, ...@@ -879,24 +1122,34 @@ angle::Result TextureMtl::redefineImage(const gl::Context *context,
ContextMtl *contextMtl = mtl::GetImpl(context); ContextMtl *contextMtl = mtl::GetImpl(context);
// Cache last defined image format: // Cache last defined image format:
mFormat = mtlFormat; mFormat = mtlFormat;
mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()]; ImageDefinitionMtl &imageDef = getImageDefinition(index);
// If actual texture exists, it means the size hasn't been changed, no need to create new image // If native texture still exists, it means the size hasn't been changed, no need to create new
if (mNativeTexture && image) // image
if (mNativeTexture && imageDef.image && imageWithinLevelRange)
{ {
ASSERT(image->textureType() == mtl::GetTextureType(GetTextureImageType(index.getType())) && ASSERT(imageDef.image->textureType() ==
image->pixelFormat() == mFormat.metalFormat && image->size() == size); mtl::GetTextureType(GetTextureImageType(index.getType())) &&
imageDef.formatID == mFormat.intendedFormatId && imageDef.image->size() == size);
} }
else else
{ {
imageDef.formatID = mtlFormat.intendedFormatId;
// Create image to hold texture's data at this level & slice: // Create image to hold texture's data at this level & slice:
switch (index.getType()) switch (index.getType())
{ {
case gl::TextureType::_2D: case gl::TextureType::_2D:
case gl::TextureType::CubeMap: case gl::TextureType::CubeMap:
ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, mtlFormat, size.width, ANGLE_TRY(mtl::Texture::Make2DTexture(
size.height, 1, false, contextMtl, mtlFormat, size.width, size.height, 1,
mFormat.hasDepthAndStencilBits(), &image)); /** renderTargetOnly */ false,
/** allowFormatView */ mFormat.hasDepthAndStencilBits(), &imageDef.image));
break;
case gl::TextureType::_3D:
ANGLE_TRY(mtl::Texture::Make3DTexture(
contextMtl, mtlFormat, size.width, size.height, size.depth, 1,
/** renderTargetOnly */ false,
/** allowFormatView */ mFormat.hasDepthAndStencilBits(), &imageDef.image));
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
...@@ -904,7 +1157,7 @@ angle::Result TextureMtl::redefineImage(const gl::Context *context, ...@@ -904,7 +1157,7 @@ angle::Result TextureMtl::redefineImage(const gl::Context *context,
} }
// Make sure emulated channels are properly initialized // Make sure emulated channels are properly initialized
ANGLE_TRY(checkForEmulatedChannels(context, mtlFormat, image)); ANGLE_TRY(checkForEmulatedChannels(context, mtlFormat, imageDef.image));
// Tell context to rebind textures // Tell context to rebind textures
contextMtl->invalidateCurrentTextures(); contextMtl->invalidateCurrentTextures();
...@@ -919,11 +1172,8 @@ angle::Result TextureMtl::setStorageImpl(const gl::Context *context, ...@@ -919,11 +1172,8 @@ angle::Result TextureMtl::setStorageImpl(const gl::Context *context,
const mtl::Format &mtlFormat, const mtl::Format &mtlFormat,
const gl::Extents &size) const gl::Extents &size)
{ {
if (mNativeTexture)
{
// Don't need to hold old images data. // Don't need to hold old images data.
releaseTexture(true); releaseTexture(true);
}
ContextMtl *contextMtl = mtl::GetImpl(context); ContextMtl *contextMtl = mtl::GetImpl(context);
...@@ -939,15 +1189,17 @@ angle::Result TextureMtl::setStorageImpl(const gl::Context *context, ...@@ -939,15 +1189,17 @@ angle::Result TextureMtl::setStorageImpl(const gl::Context *context,
angle::Result TextureMtl::setImageImpl(const gl::Context *context, angle::Result TextureMtl::setImageImpl(const gl::Context *context,
const gl::ImageIndex &index, const gl::ImageIndex &index,
const gl::InternalFormat &formatInfo, const gl::InternalFormat &dstFormatInfo,
const gl::Extents &size, const gl::Extents &size,
GLenum type, GLenum srcFormat,
GLenum srcType,
const gl::PixelUnpackState &unpack, const gl::PixelUnpackState &unpack,
gl::Buffer *unpackBuffer,
const uint8_t *pixels) const uint8_t *pixels)
{ {
ContextMtl *contextMtl = mtl::GetImpl(context); ContextMtl *contextMtl = mtl::GetImpl(context);
angle::FormatID angleFormatId = angle::FormatID angleFormatId =
angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat); angle::Format::InternalFormatToID(dstFormatInfo.sizedInternalFormat);
const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId); const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId);
ANGLE_TRY(redefineImage(context, index, mtlFormat, size)); ANGLE_TRY(redefineImage(context, index, mtlFormat, size));
...@@ -958,8 +1210,23 @@ angle::Result TextureMtl::setImageImpl(const gl::Context *context, ...@@ -958,8 +1210,23 @@ angle::Result TextureMtl::setImageImpl(const gl::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
if (context->isWebGL() && !pixels)
{
ANGLE_TRY(initializeContents(context, index));
}
// Format of the supplied pixels.
const gl::InternalFormat *srcFormatInfo;
if (srcFormat != dstFormatInfo.format || srcType != dstFormatInfo.type)
{
srcFormatInfo = &gl::GetInternalFormatInfo(srcFormat, srcType);
}
else
{
srcFormatInfo = &dstFormatInfo;
}
return setSubImageImpl(context, index, gl::Box(0, 0, 0, size.width, size.height, size.depth), return setSubImageImpl(context, index, gl::Box(0, 0, 0, size.width, size.height, size.depth),
formatInfo, type, unpack, pixels); *srcFormatInfo, srcType, unpack, unpackBuffer, pixels);
} }
angle::Result TextureMtl::setSubImageImpl(const gl::Context *context, angle::Result TextureMtl::setSubImageImpl(const gl::Context *context,
...@@ -968,82 +1235,111 @@ angle::Result TextureMtl::setSubImageImpl(const gl::Context *context, ...@@ -968,82 +1235,111 @@ angle::Result TextureMtl::setSubImageImpl(const gl::Context *context,
const gl::InternalFormat &formatInfo, const gl::InternalFormat &formatInfo,
GLenum type, GLenum type,
const gl::PixelUnpackState &unpack, const gl::PixelUnpackState &unpack,
const uint8_t *pixels) gl::Buffer *unpackBuffer,
const uint8_t *oriPixels)
{ {
if (!pixels) // NOTE(hqle): Support PBO
ASSERT(!unpackBuffer);
if (!oriPixels)
{ {
return angle::Result::Continue; return angle::Result::Continue;
} }
ASSERT(unpack.skipRows == 0 && unpack.skipPixels == 0 && unpack.skipImages == 0);
ContextMtl *contextMtl = mtl::GetImpl(context); ContextMtl *contextMtl = mtl::GetImpl(context);
angle::FormatID angleFormatId =
angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat);
const mtl::Format &mtlSrcFormat = contextMtl->getPixelFormat(angleFormatId);
if (mFormat.metalFormat != mtlSrcFormat.metalFormat)
{
ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_OPERATION);
}
ANGLE_TRY(ensureImageCreated(context, index)); ANGLE_TRY(ensureImageCreated(context, index));
mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()]; mtl::TextureRef &image = getImage(index);
GLuint sourceRowPitch = 0; GLuint sourceRowPitch = 0;
GLuint sourceDepthPitch = 0;
GLuint sourceSkipBytes = 0;
ANGLE_CHECK_GL_MATH(contextMtl, formatInfo.computeRowPitch(type, area.width, unpack.alignment, ANGLE_CHECK_GL_MATH(contextMtl, formatInfo.computeRowPitch(type, area.width, unpack.alignment,
unpack.rowLength, &sourceRowPitch)); unpack.rowLength, &sourceRowPitch));
ANGLE_CHECK_GL_MATH(
contextMtl, formatInfo.computeDepthPitch(area.height, unpack.imageHeight, sourceRowPitch,
&sourceDepthPitch));
ANGLE_CHECK_GL_MATH(contextMtl,
formatInfo.computeSkipBytes(type, sourceRowPitch, sourceDepthPitch, unpack,
index.usesTex3D(), &sourceSkipBytes));
// Check if partial image update is supported for this format // Check if partial image update is supported for this format
if (!formatInfo.supportSubImage()) if (!formatInfo.supportSubImage())
{ {
// area must be the whole mip level // area must be the whole mip level
sourceRowPitch = 0; sourceRowPitch = 0;
gl::Extents size = image->size(index); gl::Extents size = image->size();
if (area.x != 0 || area.y != 0 || area.width != size.width || area.height != size.height) if (area.x != 0 || area.y != 0 || area.width != size.width || area.height != size.height)
{ {
ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_OPERATION); ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_OPERATION);
} }
} }
// Only 2D/cube texture is supported atm
auto mtlRegion = MTLRegionMake2D(area.x, area.y, area.width, area.height);
const angle::Format &srcAngleFormat = const angle::Format &srcAngleFormat =
angle::Format::Get(angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat)); angle::Format::Get(angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat));
const uint8_t *usablePixels = oriPixels + sourceSkipBytes;
auto mtlRegion = MTLRegionMake3D(area.x, area.y, area.z, area.width, area.height, area.depth);
ANGLE_TRY(setPerSliceSubImage(context, 0, mtlRegion, formatInfo, type, srcAngleFormat,
sourceRowPitch, sourceDepthPitch, unpackBuffer, usablePixels,
image));
return angle::Result::Continue;
}
angle::Result TextureMtl::setPerSliceSubImage(const gl::Context *context,
int slice,
const MTLRegion &mtlArea,
const gl::InternalFormat &internalFormat,
GLenum type,
const angle::Format &pixelsAngleFormat,
size_t pixelsRowPitch,
size_t pixelsDepthPitch,
gl::Buffer *unpackBuffer,
const uint8_t *pixels,
const mtl::TextureRef &image)
{
// If source pixels are luminance or RGB8, we need to convert them to RGBA // If source pixels are luminance or RGB8, we need to convert them to RGBA
if (mFormat.needConversion(srcAngleFormat.id)) if (mFormat.needConversion(pixelsAngleFormat.id))
{ {
return convertAndSetSubImage(context, index, mtlRegion, formatInfo, type, srcAngleFormat, return convertAndSetPerSliceSubImage(context, slice, mtlArea, internalFormat, type,
sourceRowPitch, pixels); pixelsAngleFormat, pixelsRowPitch, pixelsDepthPitch,
unpackBuffer, pixels, image);
} }
// Upload to texture // NOTE(hqle): Support PBO
ANGLE_TRY(UploadTextureContents(context, image, mFormat.actualAngleFormat(), mtlRegion, 0, 0, ASSERT(!unpackBuffer);
pixels, sourceRowPitch));
// Upload texture data directly
ANGLE_TRY(UploadTextureContents(context, mFormat.actualAngleFormat(), mtlArea, 0, slice, pixels,
pixelsRowPitch, pixelsDepthPitch, image));
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result TextureMtl::convertAndSetSubImage(const gl::Context *context, angle::Result TextureMtl::convertAndSetPerSliceSubImage(const gl::Context *context,
const gl::ImageIndex &index, int slice,
const MTLRegion &mtlArea, const MTLRegion &mtlArea,
const gl::InternalFormat &internalFormat, const gl::InternalFormat &internalFormat,
GLenum type, GLenum type,
const angle::Format &pixelsFormat, const angle::Format &pixelsAngleFormat,
size_t pixelsRowPitch, size_t pixelsRowPitch,
const uint8_t *pixels) size_t pixelsDepthPitch,
gl::Buffer *unpackBuffer,
const uint8_t *pixels,
const mtl::TextureRef &image)
{ {
mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()];
ASSERT(image && image->valid()); ASSERT(image && image->valid());
ASSERT(image->textureType() == MTLTextureType2D);
ContextMtl *contextMtl = mtl::GetImpl(context); ContextMtl *contextMtl = mtl::GetImpl(context);
// NOTE(hqle): Support PBO
ASSERT(!unpackBuffer);
LoadImageFunctionInfo loadFunctionInfo = LoadImageFunctionInfo loadFunctionInfo =
mFormat.textureLoadFunctions ? mFormat.textureLoadFunctions(type) : LoadImageFunctionInfo(); mFormat.textureLoadFunctions ? mFormat.textureLoadFunctions(type) : LoadImageFunctionInfo();
const angle::Format &dstFormat = angle::Format::Get(mFormat.actualFormatId); const angle::Format &dstFormat = angle::Format::Get(mFormat.actualFormatId);
const size_t dstRowPitch = dstFormat.pixelBytes * mtlArea.size.width; const size_t dstRowPitch = dstFormat.pixelBytes * mtlArea.size.width;
...@@ -1059,24 +1355,28 @@ angle::Result TextureMtl::convertAndSetSubImage(const gl::Context *context, ...@@ -1059,24 +1355,28 @@ angle::Result TextureMtl::convertAndSetSubImage(const gl::Context *context,
// Decompress // Decompress
loadFunctionInfo.loadFunction(mtlArea.size.width, mtlArea.size.height, mtlArea.size.depth, loadFunctionInfo.loadFunction(mtlArea.size.width, mtlArea.size.height, mtlArea.size.depth,
pixels, pixelsRowPitch, 0, decompressBuf.data(), dstRowPitch, pixels, pixelsRowPitch, pixelsDepthPitch,
dstDepthPitch); decompressBuf.data(), dstRowPitch, dstDepthPitch);
// Upload to texture // Upload to texture
ANGLE_TRY(UploadTextureContents(context, image, dstFormat, mtlArea, 0, 0, ANGLE_TRY(UploadTextureContents(context, dstFormat, mtlArea, 0, slice, decompressBuf.data(),
decompressBuf.data(), dstRowPitch)); dstRowPitch, dstDepthPitch, image));
} // if (mFormat.intendedAngleFormat().isBlock) } // if (mFormat.intendedAngleFormat().isBlock)
else else
{ {
// Create scratch buffer // Create scratch row buffer
angle::MemoryBuffer conversionRow; angle::MemoryBuffer conversionRow;
ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch)); ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch));
// Convert row by row:
MTLRegion mtlRow = mtlArea; MTLRegion mtlRow = mtlArea;
mtlRow.size.height = 1; mtlRow.size.height = mtlRow.size.depth = 1;
const uint8_t *psrc = pixels; for (NSUInteger d = 0; d < mtlArea.size.depth; ++d)
for (NSUInteger r = 0; r < mtlArea.size.height; ++r, psrc += pixelsRowPitch)
{ {
mtlRow.origin.z = mtlArea.origin.z + d;
for (NSUInteger r = 0; r < mtlArea.size.height; ++r)
{
const uint8_t *psrc = pixels + d * pixelsDepthPitch + r * pixelsRowPitch;
mtlRow.origin.y = mtlArea.origin.y + r; mtlRow.origin.y = mtlArea.origin.y + r;
// Convert pixels // Convert pixels
...@@ -1085,20 +1385,28 @@ angle::Result TextureMtl::convertAndSetSubImage(const gl::Context *context, ...@@ -1085,20 +1385,28 @@ angle::Result TextureMtl::convertAndSetSubImage(const gl::Context *context,
loadFunctionInfo.loadFunction(mtlRow.size.width, 1, 1, psrc, pixelsRowPitch, 0, loadFunctionInfo.loadFunction(mtlRow.size.width, 1, 1, psrc, pixelsRowPitch, 0,
conversionRow.data(), dstRowPitch, 0); conversionRow.data(), dstRowPitch, 0);
} }
else if (mFormat.hasDepthOrStencilBits())
{
ConvertDepthStencilData(mtlRow.size, pixelsAngleFormat, pixelsRowPitch, 0, psrc,
dstFormat, nullptr, dstRowPitch, 0,
conversionRow.data());
}
else else
{ {
CopyImageCHROMIUM(psrc, pixelsRowPitch, pixelsFormat.pixelBytes, 0, CopyImageCHROMIUM(psrc, pixelsRowPitch, pixelsAngleFormat.pixelBytes, 0,
pixelsFormat.pixelReadFunction, conversionRow.data(), dstRowPitch, pixelsAngleFormat.pixelReadFunction, conversionRow.data(),
dstFormat.pixelBytes, 0, dstFormat.pixelWriteFunction, dstRowPitch, dstFormat.pixelBytes, 0,
internalFormat.format, dstFormat.componentType, mtlRow.size.width, dstFormat.pixelWriteFunction, internalFormat.format,
1, 1, false, false, false); dstFormat.componentType, mtlRow.size.width, 1, 1, false,
false, false);
} }
// Upload to texture // Upload to texture
ANGLE_TRY(UploadTextureContents(context, image, dstFormat, mtlRow, 0, 0, ANGLE_TRY(UploadTextureContents(context, dstFormat, mtlRow, 0, slice,
conversionRow.data(), dstRowPitch)); conversionRow.data(), dstRowPitch, 0, image));
} }
} }
} // if (mFormat.intendedAngleFormat().isBlock)
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -1115,22 +1423,12 @@ angle::Result TextureMtl::checkForEmulatedChannels(const gl::Context *context, ...@@ -1115,22 +1423,12 @@ angle::Result TextureMtl::checkForEmulatedChannels(const gl::Context *context,
{ {
uint32_t mipmaps = texture->mipmapLevels(); uint32_t mipmaps = texture->mipmapLevels();
int layers = texture->textureType() == MTLTextureTypeCube ? 6 : 1; uint32_t layers = texture->cubeFacesOrArrayLength();
for (int layer = 0; layer < layers; ++layer) for (uint32_t layer = 0; layer < layers; ++layer)
{ {
auto cubeFace = static_cast<gl::TextureTarget>(
static_cast<int>(gl::TextureTarget::CubeMapPositiveX) + layer);
for (uint32_t mip = 0; mip < mipmaps; ++mip) for (uint32_t mip = 0; mip < mipmaps; ++mip)
{ {
gl::ImageIndex index; gl::ImageIndex index = GetSliceMipIndex(texture, layer, mip);
if (layers > 1)
{
index = gl::ImageIndex::MakeCubeMapFace(cubeFace, mip);
}
else
{
index = gl::ImageIndex::Make2D(mip);
}
ANGLE_TRY(mtl::InitializeTextureContents(context, texture, mFormat, index)); ANGLE_TRY(mtl::InitializeTextureContents(context, texture, mFormat, index));
} }
...@@ -1142,12 +1440,9 @@ angle::Result TextureMtl::checkForEmulatedChannels(const gl::Context *context, ...@@ -1142,12 +1440,9 @@ angle::Result TextureMtl::checkForEmulatedChannels(const gl::Context *context,
angle::Result TextureMtl::initializeContents(const gl::Context *context, angle::Result TextureMtl::initializeContents(const gl::Context *context,
const gl::ImageIndex &index) const gl::ImageIndex &index)
{ {
mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()]; ANGLE_TRY(ensureImageCreated(context, index));
if (!image) mtl::TextureRef &image = getImage(index);
{ return mtl::InitializeTextureContents(context, image, mFormat, GetZeroLevelIndex(image));
ANGLE_TRY(ensureTextureCreated(context));
}
return mtl::InitializeTextureContents(context, image, mFormat, GetImageBaseLevelIndex(image));
} }
angle::Result TextureMtl::copySubImageImpl(const gl::Context *context, angle::Result TextureMtl::copySubImageImpl(const gl::Context *context,
...@@ -1203,15 +1498,12 @@ angle::Result TextureMtl::copySubImageWithDraw(const gl::Context *context, ...@@ -1203,15 +1498,12 @@ angle::Result TextureMtl::copySubImageWithDraw(const gl::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()]; const RenderTargetMtl &imageRtt = getRenderTarget(index);
ASSERT(image && image->valid());
RenderTargetMtl writeRtt; mtl::RenderCommandEncoder *cmdEncoder = contextMtl->getRenderTargetCommandEncoder(imageRtt);
writeRtt.set(image, 0, 0, mFormat);
mtl::RenderCommandEncoder *cmdEncoder = contextMtl->getRenderTargetCommandEncoder(writeRtt);
mtl::ColorBlitParams blitParams; mtl::ColorBlitParams blitParams;
blitParams.dstTextureSize = image->size(0); blitParams.dstTextureSize = imageRtt.getTexture()->size(imageRtt.getLevelIndex());
blitParams.dstRect = gl::Rectangle(modifiedDestOffset.x, modifiedDestOffset.y, blitParams.dstRect = gl::Rectangle(modifiedDestOffset.x, modifiedDestOffset.y,
clippedSourceArea.width, clippedSourceArea.height); clippedSourceArea.width, clippedSourceArea.height);
blitParams.dstScissorRect = blitParams.dstRect; blitParams.dstScissorRect = blitParams.dstRect;
...@@ -1235,7 +1527,7 @@ angle::Result TextureMtl::copySubImageCPU(const gl::Context *context, ...@@ -1235,7 +1527,7 @@ angle::Result TextureMtl::copySubImageCPU(const gl::Context *context,
const gl::InternalFormat &internalFormat, const gl::InternalFormat &internalFormat,
gl::Framebuffer *source) gl::Framebuffer *source)
{ {
mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()]; mtl::TextureRef &image = getImage(index);
ASSERT(image && image->valid()); ASSERT(image && image->valid());
ContextMtl *contextMtl = mtl::GetImpl(context); ContextMtl *contextMtl = mtl::GetImpl(context);
...@@ -1253,9 +1545,25 @@ angle::Result TextureMtl::copySubImageCPU(const gl::Context *context, ...@@ -1253,9 +1545,25 @@ angle::Result TextureMtl::copySubImageCPU(const gl::Context *context,
angle::MemoryBuffer conversionRow; angle::MemoryBuffer conversionRow;
ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch)); ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch));
MTLRegion mtlDstRowArea = MTLRegionMake2D(modifiedDestOffset.x, 0, clippedSourceArea.width, 1);
gl::Rectangle srcRowArea = gl::Rectangle(clippedSourceArea.x, 0, clippedSourceArea.width, 1); gl::Rectangle srcRowArea = gl::Rectangle(clippedSourceArea.x, 0, clippedSourceArea.width, 1);
MTLRegion mtlDstRowArea = MTLRegionMake2D(modifiedDestOffset.x, 0, clippedSourceArea.width, 1);
uint32_t dstSlice = 0;
switch (index.getType())
{
case gl::TextureType::_2D:
case gl::TextureType::CubeMap:
dstSlice = 0;
break;
case gl::TextureType::_3D:
ASSERT(index.hasLayer());
dstSlice = 0;
mtlDstRowArea.origin.z = index.getLayerIndex();
break;
default:
UNREACHABLE();
}
// Copy row by row:
for (int r = 0; r < clippedSourceArea.height; ++r) for (int r = 0; r < clippedSourceArea.height; ++r)
{ {
mtlDstRowArea.origin.y = modifiedDestOffset.y + r; mtlDstRowArea.origin.y = modifiedDestOffset.y + r;
...@@ -1267,11 +1575,12 @@ angle::Result TextureMtl::copySubImageCPU(const gl::Context *context, ...@@ -1267,11 +1575,12 @@ angle::Result TextureMtl::copySubImageCPU(const gl::Context *context,
gl::Rectangle flippedSrcRowArea = gl::Rectangle flippedSrcRowArea =
framebufferMtl->getCorrectFlippedReadArea(context, srcRowArea); framebufferMtl->getCorrectFlippedReadArea(context, srcRowArea);
ANGLE_TRY(framebufferMtl->readPixelsImpl(context, flippedSrcRowArea, packParams, ANGLE_TRY(framebufferMtl->readPixelsImpl(context, flippedSrcRowArea, packParams,
colorReadRT, conversionRow.data())); framebufferMtl->getColorReadRenderTarget(context),
conversionRow.data()));
// Upload to texture // Upload to texture
ANGLE_TRY(UploadTextureContents(context, image, dstFormat, mtlDstRowArea, 0, 0, ANGLE_TRY(UploadTextureContents(context, dstFormat, mtlDstRowArea, 0, dstSlice,
conversionRow.data(), dstRowPitch)); conversionRow.data(), dstRowPitch, 0, image));
} }
return angle::Result::Continue; return angle::Result::Continue;
...@@ -1300,24 +1609,20 @@ angle::Result TextureMtl::copySubTextureImpl(const gl::Context *context, ...@@ -1300,24 +1609,20 @@ angle::Result TextureMtl::copySubTextureImpl(const gl::Context *context,
ANGLE_TRY(sourceMtl->ensureImageCreated(context, sourceIndex)); ANGLE_TRY(sourceMtl->ensureImageCreated(context, sourceIndex));
const gl::ImageDesc &srcDesc = source->getState().getImageDesc(sourceIndex); const ImageDefinitionMtl &srcImageDef = sourceMtl->getImageDefinition(sourceIndex);
const mtl::TextureRef &srcImage = sourceMtl->mTexImages[0][sourceLevel]; const mtl::TextureRef &sourceImage = srcImageDef.image;
const mtl::Format &srcFormat = contextMtl->getPixelFormat( const mtl::Format &sourceFormat = contextMtl->getPixelFormat(srcImageDef.formatID);
angle::Format::InternalFormatToID(srcDesc.format.info->sizedInternalFormat)); const angle::Format &sourceAngleFormat = sourceFormat.actualAngleFormat();
const angle::Format &srcAngleFormat = srcFormat.actualAngleFormat();
const mtl::Format &dstFormat = contextMtl->getPixelFormat( if (!mFormat.getCaps().isRenderable())
angle::Format::InternalFormatToID(internalFormat.sizedInternalFormat));
if (!dstFormat.getCaps().isRenderable())
{ {
return copySubTextureCPU(context, index, destOffset, internalFormat, 0, sourceBox, return copySubTextureCPU(context, index, destOffset, internalFormat, 0, sourceBox,
srcAngleFormat, unpackFlipY, unpackPremultiplyAlpha, sourceAngleFormat, unpackFlipY, unpackPremultiplyAlpha,
unpackUnmultiplyAlpha, srcImage); unpackUnmultiplyAlpha, sourceImage);
} }
return copySubTextureWithDraw(context, index, destOffset, internalFormat, 0, sourceBox, return copySubTextureWithDraw(context, index, destOffset, internalFormat, 0, sourceBox,
srcAngleFormat, unpackFlipY, unpackPremultiplyAlpha, sourceAngleFormat, unpackFlipY, unpackPremultiplyAlpha,
unpackUnmultiplyAlpha, srcImage); unpackUnmultiplyAlpha, sourceImage);
} }
angle::Result TextureMtl::copySubTextureWithDraw(const gl::Context *context, angle::Result TextureMtl::copySubTextureWithDraw(const gl::Context *context,
...@@ -1335,7 +1640,7 @@ angle::Result TextureMtl::copySubTextureWithDraw(const gl::Context *context, ...@@ -1335,7 +1640,7 @@ angle::Result TextureMtl::copySubTextureWithDraw(const gl::Context *context,
ContextMtl *contextMtl = mtl::GetImpl(context); ContextMtl *contextMtl = mtl::GetImpl(context);
DisplayMtl *displayMtl = contextMtl->getDisplay(); DisplayMtl *displayMtl = contextMtl->getDisplay();
mtl::TextureRef image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()]; mtl::TextureRef image = getImage(index);
ASSERT(image && image->valid()); ASSERT(image && image->valid());
if (internalFormat.colorEncoding == GL_SRGB) if (internalFormat.colorEncoding == GL_SRGB)
...@@ -1344,7 +1649,7 @@ angle::Result TextureMtl::copySubTextureWithDraw(const gl::Context *context, ...@@ -1344,7 +1649,7 @@ angle::Result TextureMtl::copySubTextureWithDraw(const gl::Context *context,
} }
mtl::RenderCommandEncoder *cmdEncoder = mtl::RenderCommandEncoder *cmdEncoder =
contextMtl->getTextureRenderCommandEncoder(image, GetImageBaseLevelIndex(image)); contextMtl->getTextureRenderCommandEncoder(image, GetZeroLevelIndex(image));
mtl::ColorBlitParams blitParams; mtl::ColorBlitParams blitParams;
blitParams.dstTextureSize = image->size(); blitParams.dstTextureSize = image->size();
...@@ -1379,7 +1684,7 @@ angle::Result TextureMtl::copySubTextureCPU(const gl::Context *context, ...@@ -1379,7 +1684,7 @@ angle::Result TextureMtl::copySubTextureCPU(const gl::Context *context,
bool unpackUnmultiplyAlpha, bool unpackUnmultiplyAlpha,
const mtl::TextureRef &sourceTexture) const mtl::TextureRef &sourceTexture)
{ {
mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()]; mtl::TextureRef &image = getImage(index);
ASSERT(image && image->valid()); ASSERT(image && image->valid());
ContextMtl *contextMtl = mtl::GetImpl(context); ContextMtl *contextMtl = mtl::GetImpl(context);
...@@ -1399,7 +1704,7 @@ angle::Result TextureMtl::copySubTextureCPU(const gl::Context *context, ...@@ -1399,7 +1704,7 @@ angle::Result TextureMtl::copySubTextureCPU(const gl::Context *context,
MTLRegionMake2D(destOffset.x, destOffset.y, sourceBox.width, sourceBox.height); MTLRegionMake2D(destOffset.x, destOffset.y, sourceBox.width, sourceBox.height);
// Read pixels from source to memory: // Read pixels from source to memory:
sourceTexture->getBytes(contextMtl, srcRowPitch, mtlSrcArea, sourceNativeLevel, sourceTexture->getBytes(contextMtl, srcRowPitch, 0, mtlSrcArea, sourceNativeLevel, 0,
conversionSrc.data()); conversionSrc.data());
// Convert to destination format // Convert to destination format
...@@ -1411,8 +1716,8 @@ angle::Result TextureMtl::copySubTextureCPU(const gl::Context *context, ...@@ -1411,8 +1716,8 @@ angle::Result TextureMtl::copySubTextureCPU(const gl::Context *context,
unpackUnmultiplyAlpha); unpackUnmultiplyAlpha);
// Upload to texture // Upload to texture
ANGLE_TRY(UploadTextureContents(context, image, dstAngleFormat, mtlDstArea, 0, 0, ANGLE_TRY(UploadTextureContents(context, dstAngleFormat, mtlDstArea, 0, 0, conversionDst.data(),
conversionDst.data(), convRowPitch)); convRowPitch, 0, image));
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -491,12 +491,11 @@ class BlitCommandEncoder final : public CommandEncoder ...@@ -491,12 +491,11 @@ class BlitCommandEncoder final : public CommandEncoder
BlitCommandEncoder &copyTexture(const TextureRef &src, BlitCommandEncoder &copyTexture(const TextureRef &src,
uint32_t srcSlice, uint32_t srcSlice,
uint32_t srcLevel, uint32_t srcLevel,
MTLOrigin srcOrigin,
MTLSize srcSize,
const TextureRef &dst, const TextureRef &dst,
uint32_t dstSlice, uint32_t dstSlice,
uint32_t dstLevel, uint32_t dstLevel,
MTLOrigin dstOrigin); uint32_t sliceCount,
uint32_t levelCount);
BlitCommandEncoder &generateMipmapsForTexture(const TextureRef &texture); BlitCommandEncoder &generateMipmapsForTexture(const TextureRef &texture);
BlitCommandEncoder &synchronizeResource(const TextureRef &texture); BlitCommandEncoder &synchronizeResource(const TextureRef &texture);
......
...@@ -1534,14 +1534,13 @@ BlitCommandEncoder &BlitCommandEncoder::copyBufferToTexture(const BufferRef &src ...@@ -1534,14 +1534,13 @@ BlitCommandEncoder &BlitCommandEncoder::copyBufferToTexture(const BufferRef &src
} }
BlitCommandEncoder &BlitCommandEncoder::copyTexture(const TextureRef &src, BlitCommandEncoder &BlitCommandEncoder::copyTexture(const TextureRef &src,
uint32_t srcSlice, uint32_t srcStartSlice,
uint32_t srcLevel, uint32_t srcStartLevel,
MTLOrigin srcOrigin,
MTLSize srcSize,
const TextureRef &dst, const TextureRef &dst,
uint32_t dstSlice, uint32_t dstStartSlice,
uint32_t dstLevel, uint32_t dstStartLevel,
MTLOrigin dstOrigin) uint32_t sliceCount,
uint32_t levelCount)
{ {
if (!src || !dst) if (!src || !dst)
{ {
...@@ -1550,15 +1549,30 @@ BlitCommandEncoder &BlitCommandEncoder::copyTexture(const TextureRef &src, ...@@ -1550,15 +1549,30 @@ BlitCommandEncoder &BlitCommandEncoder::copyTexture(const TextureRef &src,
cmdBuffer().setReadDependency(src); cmdBuffer().setReadDependency(src);
cmdBuffer().setWriteDependency(dst); cmdBuffer().setWriteDependency(dst);
MTLOrigin origin = MTLOriginMake(0, 0, 0);
for (uint32_t slice = 0; slice < sliceCount; ++slice)
{
uint32_t srcSlice = srcStartSlice + slice;
uint32_t dstSlice = dstStartSlice + slice;
for (uint32_t level = 0; level < levelCount; ++level)
{
uint32_t srcLevel = srcStartLevel + level;
uint32_t dstLevel = dstStartLevel + level;
MTLSize srcSize =
MTLSizeMake(src->width(srcLevel), src->height(srcLevel), src->depth(srcLevel));
[get() copyFromTexture:src->get() [get() copyFromTexture:src->get()
sourceSlice:srcSlice sourceSlice:srcSlice
sourceLevel:srcLevel sourceLevel:srcLevel
sourceOrigin:srcOrigin sourceOrigin:origin
sourceSize:srcSize sourceSize:srcSize
toTexture:dst->get() toTexture:dst->get()
destinationSlice:dstSlice destinationSlice:dstSlice
destinationLevel:dstLevel destinationLevel:dstLevel
destinationOrigin:dstOrigin]; destinationOrigin:origin];
}
}
return *this; return *this;
} }
......
...@@ -120,6 +120,16 @@ class Texture final : public Resource, ...@@ -120,6 +120,16 @@ class Texture final : public Resource,
bool allowFormatView, bool allowFormatView,
TextureRef *refOut); TextureRef *refOut);
static angle::Result Make3DTexture(ContextMtl *context,
const Format &format,
uint32_t width,
uint32_t height,
uint32_t depth,
uint32_t mips,
bool renderTargetOnly,
bool allowFormatView,
TextureRef *refOut);
static TextureRef MakeFromMetal(id<MTLTexture> metalTexture); static TextureRef MakeFromMetal(id<MTLTexture> metalTexture);
// Allow CPU to read & write data directly to this texture? // Allow CPU to read & write data directly to this texture?
...@@ -127,24 +137,35 @@ class Texture final : public Resource, ...@@ -127,24 +137,35 @@ class Texture final : public Resource,
bool supportFormatView() const; bool supportFormatView() const;
void replaceRegion(ContextMtl *context, void replace2DRegion(ContextMtl *context,
MTLRegion region, const MTLRegion &region,
uint32_t mipmapLevel, uint32_t mipmapLevel,
uint32_t slice, uint32_t slice,
const uint8_t *data, const uint8_t *data,
size_t bytesPerRow); size_t bytesPerRow);
// read pixel data from slice 0 void replaceRegion(ContextMtl *context,
const MTLRegion &region,
uint32_t mipmapLevel,
uint32_t slice,
const uint8_t *data,
size_t bytesPerRow,
size_t bytesPer2DImage);
void getBytes(ContextMtl *context, void getBytes(ContextMtl *context,
size_t bytesPerRow, size_t bytesPerRow,
MTLRegion region, size_t bytesPer2DInage,
const MTLRegion &region,
uint32_t mipmapLevel, uint32_t mipmapLevel,
uint32_t slice,
uint8_t *dataOut); uint8_t *dataOut);
// Create 2d view of a cube face which full range of mip levels. // Create 2d view of a cube face which full range of mip levels.
TextureRef createCubeFaceView(uint32_t face); TextureRef createCubeFaceView(uint32_t face);
// Create a view of one slice at a level. // Create a view of one slice at a level.
TextureRef createSliceMipView(uint32_t slice, uint32_t level); TextureRef createSliceMipView(uint32_t slice, uint32_t level);
// Create a view of a level.
TextureRef createMipView(uint32_t level);
// Create a view with different format // Create a view with different format
TextureRef createViewWithDifferentFormat(MTLPixelFormat format); TextureRef createViewWithDifferentFormat(MTLPixelFormat format);
// Same as above but the target format must be compatible, for example sRGB to linear. In this // Same as above but the target format must be compatible, for example sRGB to linear. In this
...@@ -155,6 +176,8 @@ class Texture final : public Resource, ...@@ -155,6 +176,8 @@ class Texture final : public Resource,
MTLPixelFormat pixelFormat() const; MTLPixelFormat pixelFormat() const;
uint32_t mipmapLevels() const; uint32_t mipmapLevels() const;
uint32_t arrayLength() const;
uint32_t cubeFacesOrArrayLength() const;
uint32_t width(uint32_t level = 0) const; uint32_t width(uint32_t level = 0) const;
uint32_t height(uint32_t level = 0) const; uint32_t height(uint32_t level = 0) const;
...@@ -201,7 +224,7 @@ class Texture final : public Resource, ...@@ -201,7 +224,7 @@ class Texture final : public Resource,
// Create a texture view // Create a texture view
Texture(Texture *original, MTLPixelFormat format); Texture(Texture *original, MTLPixelFormat format);
Texture(Texture *original, MTLTextureType type, NSRange mipmapLevelRange, uint32_t slice); Texture(Texture *original, MTLTextureType type, NSRange mipmapLevelRange, NSRange slices);
void syncContent(ContextMtl *context); void syncContent(ContextMtl *context);
......
...@@ -149,6 +149,37 @@ angle::Result Texture::Make2DMSTexture(ContextMtl *context, ...@@ -149,6 +149,37 @@ angle::Result Texture::Make2DMSTexture(ContextMtl *context,
} }
/** static */ /** static */
angle::Result Texture::Make3DTexture(ContextMtl *context,
const Format &format,
uint32_t width,
uint32_t height,
uint32_t depth,
uint32_t mips,
bool renderTargetOnly,
bool allowFormatView,
TextureRef *refOut)
{
ANGLE_MTL_OBJC_SCOPE
{
// Use texture2DDescriptorWithPixelFormat to calculate full range mipmap range:
uint32_t maxDimen = std::max(width, height);
maxDimen = std::max(maxDimen, depth);
MTLTextureDescriptor *desc =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format.metalFormat
width:maxDimen
height:maxDimen
mipmapped:mips == 0 || mips > 1];
desc.textureType = MTLTextureType3D;
desc.width = width;
desc.height = height;
desc.depth = depth;
return MakeTexture(context, format, desc, mips, renderTargetOnly, allowFormatView, refOut);
} // ANGLE_MTL_OBJC_SCOPE
}
/** static */
angle::Result Texture::MakeTexture(ContextMtl *context, angle::Result Texture::MakeTexture(ContextMtl *context,
const Format &mtlFormat, const Format &mtlFormat,
MTLTextureDescriptor *desc, MTLTextureDescriptor *desc,
...@@ -242,7 +273,7 @@ Texture::Texture(Texture *original, MTLPixelFormat format) ...@@ -242,7 +273,7 @@ Texture::Texture(Texture *original, MTLPixelFormat format)
} }
} }
Texture::Texture(Texture *original, MTLTextureType type, NSRange mipmapLevelRange, uint32_t slice) Texture::Texture(Texture *original, MTLTextureType type, NSRange mipmapLevelRange, NSRange slices)
: Resource(original), : Resource(original),
mColorWritableMask(original->mColorWritableMask) // Share color write mask property mColorWritableMask(original->mColorWritableMask) // Share color write mask property
{ {
...@@ -251,7 +282,7 @@ Texture::Texture(Texture *original, MTLTextureType type, NSRange mipmapLevelRang ...@@ -251,7 +282,7 @@ Texture::Texture(Texture *original, MTLTextureType type, NSRange mipmapLevelRang
auto view = [original->get() newTextureViewWithPixelFormat:original->pixelFormat() auto view = [original->get() newTextureViewWithPixelFormat:original->pixelFormat()
textureType:type textureType:type
levels:mipmapLevelRange levels:mipmapLevelRange
slices:NSMakeRange(slice, 1)]; slices:slices];
set([view ANGLE_MTL_AUTORELEASE]); set([view ANGLE_MTL_AUTORELEASE]);
} }
...@@ -299,13 +330,25 @@ bool Texture::supportFormatView() const ...@@ -299,13 +330,25 @@ bool Texture::supportFormatView() const
return get().usage & MTLTextureUsagePixelFormatView; return get().usage & MTLTextureUsagePixelFormatView;
} }
void Texture::replaceRegion(ContextMtl *context, void Texture::replace2DRegion(ContextMtl *context,
MTLRegion region, const MTLRegion &region,
uint32_t mipmapLevel, uint32_t mipmapLevel,
uint32_t slice, uint32_t slice,
const uint8_t *data, const uint8_t *data,
size_t bytesPerRow) size_t bytesPerRow)
{ {
ASSERT(region.size.depth == 1);
replaceRegion(context, region, mipmapLevel, slice, data, bytesPerRow, 0);
}
void Texture::replaceRegion(ContextMtl *context,
const MTLRegion &region,
uint32_t mipmapLevel,
uint32_t slice,
const uint8_t *data,
size_t bytesPerRow,
size_t bytesPer2DImage)
{
if (mipmapLevel >= this->mipmapLevels()) if (mipmapLevel >= this->mipmapLevels())
{ {
return; return;
...@@ -325,18 +368,25 @@ void Texture::replaceRegion(ContextMtl *context, ...@@ -325,18 +368,25 @@ void Texture::replaceRegion(ContextMtl *context,
cmdQueue.ensureResourceReadyForCPU(this); cmdQueue.ensureResourceReadyForCPU(this);
if (textureType() != MTLTextureType3D)
{
bytesPer2DImage = 0;
}
[get() replaceRegion:region [get() replaceRegion:region
mipmapLevel:mipmapLevel mipmapLevel:mipmapLevel
slice:slice slice:slice
withBytes:data withBytes:data
bytesPerRow:bytesPerRow bytesPerRow:bytesPerRow
bytesPerImage:0]; bytesPerImage:bytesPer2DImage];
} }
void Texture::getBytes(ContextMtl *context, void Texture::getBytes(ContextMtl *context,
size_t bytesPerRow, size_t bytesPerRow,
MTLRegion region, size_t bytesPer2DInage,
const MTLRegion &region,
uint32_t mipmapLevel, uint32_t mipmapLevel,
uint32_t slice,
uint8_t *dataOut) uint8_t *dataOut)
{ {
ASSERT(isCPUAccessible()); ASSERT(isCPUAccessible());
...@@ -353,7 +403,12 @@ void Texture::getBytes(ContextMtl *context, ...@@ -353,7 +403,12 @@ void Texture::getBytes(ContextMtl *context,
cmdQueue.ensureResourceReadyForCPU(this); cmdQueue.ensureResourceReadyForCPU(this);
[get() getBytes:dataOut bytesPerRow:bytesPerRow fromRegion:region mipmapLevel:mipmapLevel]; [get() getBytes:dataOut
bytesPerRow:bytesPerRow
bytesPerImage:bytesPer2DInage
fromRegion:region
mipmapLevel:mipmapLevel
slice:slice];
} }
TextureRef Texture::createCubeFaceView(uint32_t face) TextureRef Texture::createCubeFaceView(uint32_t face)
...@@ -363,8 +418,8 @@ TextureRef Texture::createCubeFaceView(uint32_t face) ...@@ -363,8 +418,8 @@ TextureRef Texture::createCubeFaceView(uint32_t face)
switch (textureType()) switch (textureType())
{ {
case MTLTextureTypeCube: case MTLTextureTypeCube:
return TextureRef( return TextureRef(new Texture(
new Texture(this, MTLTextureType2D, NSMakeRange(0, mipmapLevels()), face)); this, MTLTextureType2D, NSMakeRange(0, mipmapLevels()), NSMakeRange(face, 1)));
default: default:
UNREACHABLE(); UNREACHABLE();
return nullptr; return nullptr;
...@@ -380,8 +435,9 @@ TextureRef Texture::createSliceMipView(uint32_t slice, uint32_t level) ...@@ -380,8 +435,9 @@ TextureRef Texture::createSliceMipView(uint32_t slice, uint32_t level)
{ {
case MTLTextureTypeCube: case MTLTextureTypeCube:
case MTLTextureType2D: case MTLTextureType2D:
return TextureRef( case MTLTextureType2DArray:
new Texture(this, MTLTextureType2D, NSMakeRange(level, 1), slice)); return TextureRef(new Texture(this, MTLTextureType2D, NSMakeRange(level, 1),
NSMakeRange(slice, 1)));
default: default:
UNREACHABLE(); UNREACHABLE();
return nullptr; return nullptr;
...@@ -389,6 +445,16 @@ TextureRef Texture::createSliceMipView(uint32_t slice, uint32_t level) ...@@ -389,6 +445,16 @@ TextureRef Texture::createSliceMipView(uint32_t slice, uint32_t level)
} }
} }
TextureRef Texture::createMipView(uint32_t level)
{
ANGLE_MTL_OBJC_SCOPE
{
NSUInteger slices = cubeFacesOrArrayLength();
return TextureRef(
new Texture(this, textureType(), NSMakeRange(level, 1), NSMakeRange(0, slices)));
}
}
TextureRef Texture::createViewWithDifferentFormat(MTLPixelFormat format) TextureRef Texture::createViewWithDifferentFormat(MTLPixelFormat format)
{ {
ASSERT(supportFormatView()); ASSERT(supportFormatView());
...@@ -415,6 +481,20 @@ uint32_t Texture::mipmapLevels() const ...@@ -415,6 +481,20 @@ uint32_t Texture::mipmapLevels() const
return static_cast<uint32_t>(get().mipmapLevelCount); return static_cast<uint32_t>(get().mipmapLevelCount);
} }
uint32_t Texture::arrayLength() const
{
return static_cast<uint32_t>(get().arrayLength);
}
uint32_t Texture::cubeFacesOrArrayLength() const
{
if (textureType() == MTLTextureTypeCube)
{
return 6;
}
return arrayLength();
}
uint32_t Texture::width(uint32_t level) const uint32_t Texture::width(uint32_t level) const
{ {
return static_cast<uint32_t>(GetMipSize(get().width, level)); return static_cast<uint32_t>(GetMipSize(get().width, level));
...@@ -443,10 +523,14 @@ gl::Extents Texture::size(uint32_t level) const ...@@ -443,10 +523,14 @@ gl::Extents Texture::size(uint32_t level) const
gl::Extents Texture::size(const gl::ImageIndex &index) const gl::Extents Texture::size(const gl::ImageIndex &index) const
{ {
// Only support these texture types for now gl::Extents extents = size(index.getLevelIndex());
ASSERT(!get() || textureType() == MTLTextureType2D || textureType() == MTLTextureTypeCube);
if (index.hasLayer())
{
extents.depth = 1;
}
return size(index.getLevelIndex()); return extents;
} }
uint32_t Texture::samples() const uint32_t Texture::samples() const
......
...@@ -183,6 +183,7 @@ void BaseRenderPassAttachmentDescToObjC(const RenderPassAttachmentDesc &src, ...@@ -183,6 +183,7 @@ void BaseRenderPassAttachmentDescToObjC(const RenderPassAttachmentDesc &src,
dst.texture = ToObjC(implicitMsTexture); dst.texture = ToObjC(implicitMsTexture);
dst.level = 0; dst.level = 0;
dst.slice = 0; dst.slice = 0;
dst.depthPlane = 0;
dst.resolveTexture = ToObjC(src.texture); dst.resolveTexture = ToObjC(src.texture);
dst.resolveLevel = src.level; dst.resolveLevel = src.level;
if (dst.resolveTexture.textureType == MTLTextureType3D) if (dst.resolveTexture.textureType == MTLTextureType3D)
...@@ -213,6 +214,7 @@ void BaseRenderPassAttachmentDescToObjC(const RenderPassAttachmentDesc &src, ...@@ -213,6 +214,7 @@ void BaseRenderPassAttachmentDescToObjC(const RenderPassAttachmentDesc &src,
dst.resolveTexture = nil; dst.resolveTexture = nil;
dst.resolveLevel = 0; dst.resolveLevel = 0;
dst.resolveSlice = 0; dst.resolveSlice = 0;
dst.resolveDepthPlane = 0;
} }
ANGLE_OBJC_CP_PROPERTY(dst, src, loadAction); ANGLE_OBJC_CP_PROPERTY(dst, src, loadAction);
......
...@@ -41,6 +41,15 @@ angle::Result InitializeTextureContentsGPU(const gl::Context *context, ...@@ -41,6 +41,15 @@ angle::Result InitializeTextureContentsGPU(const gl::Context *context,
const gl::ImageIndex &index, const gl::ImageIndex &index,
MTLColorWriteMask channelsToInit); MTLColorWriteMask channelsToInit);
// Unified texture's per slice/depth texel reading function
angle::Result ReadTexturePerSliceBytes(const gl::Context *context,
const TextureRef &texture,
size_t bytesPerRow,
const gl::Rectangle &fromRegion,
uint32_t mipLevel,
uint32_t sliceOrDepth,
uint8_t *dataOut);
MTLViewport GetViewport(const gl::Rectangle &rect, double znear = 0, double zfar = 1); MTLViewport GetViewport(const gl::Rectangle &rect, double znear = 0, double zfar = 1);
MTLViewport GetViewportFlipY(const gl::Rectangle &rect, MTLViewport GetViewportFlipY(const gl::Rectangle &rect,
NSUInteger screenHeight, NSUInteger screenHeight,
......
...@@ -64,6 +64,38 @@ uint32_t GetDeviceVendorIdFromIOKit(id<MTLDevice> device) ...@@ -64,6 +64,38 @@ uint32_t GetDeviceVendorIdFromIOKit(id<MTLDevice> device)
} }
#endif #endif
void GetSliceAndDepth(const gl::ImageIndex &index, GLint *layer, GLint *startDepth)
{
*layer = *startDepth = 0;
if (!index.hasLayer())
{
return;
}
switch (index.getType())
{
case gl::TextureType::CubeMap:
*layer = index.cubeMapFaceIndex();
break;
case gl::TextureType::_2DArray:
*layer = index.getLayerIndex();
break;
case gl::TextureType::_3D:
*startDepth = index.getLayerIndex();
break;
default:
UNREACHABLE();
break;
}
}
GLint GetSliceOrDepth(const gl::ImageIndex &index)
{
GLint layer, startDepth;
GetSliceAndDepth(index, &layer, &startDepth);
return std::max(layer, startDepth);
}
} }
angle::Result InitializeTextureContents(const gl::Context *context, angle::Result InitializeTextureContents(const gl::Context *context,
...@@ -72,8 +104,6 @@ angle::Result InitializeTextureContents(const gl::Context *context, ...@@ -72,8 +104,6 @@ angle::Result InitializeTextureContents(const gl::Context *context,
const gl::ImageIndex &index) const gl::ImageIndex &index)
{ {
ASSERT(texture && texture->valid()); ASSERT(texture && texture->valid());
ASSERT(texture->textureType() == MTLTextureType2D ||
texture->textureType() == MTLTextureTypeCube);
ContextMtl *contextMtl = mtl::GetImpl(context); ContextMtl *contextMtl = mtl::GetImpl(context);
const gl::InternalFormat &intendedInternalFormat = textureObjFormat.intendedInternalFormat(); const gl::InternalFormat &intendedInternalFormat = textureObjFormat.intendedInternalFormat();
...@@ -88,11 +118,13 @@ angle::Result InitializeTextureContents(const gl::Context *context, ...@@ -88,11 +118,13 @@ angle::Result InitializeTextureContents(const gl::Context *context,
gl::Extents size = texture->size(index); gl::Extents size = texture->size(index);
// Initialize the content to black // Intiialize the content to black
GLint layer, startDepth;
GetSliceAndDepth(index, &layer, &startDepth);
if (texture->isCPUAccessible() && index.getType() != gl::TextureType::_2DMultisample && if (texture->isCPUAccessible() && index.getType() != gl::TextureType::_2DMultisample &&
index.getType() != gl::TextureType::_2DMultisampleArray) index.getType() != gl::TextureType::_2DMultisampleArray)
{ {
const angle::Format &dstFormat = angle::Format::Get(textureObjFormat.actualFormatId); const angle::Format &dstFormat = angle::Format::Get(textureObjFormat.actualFormatId);
const size_t dstRowPitch = dstFormat.pixelBytes * size.width; const size_t dstRowPitch = dstFormat.pixelBytes * size.width;
angle::MemoryBuffer conversionRow; angle::MemoryBuffer conversionRow;
...@@ -121,16 +153,19 @@ angle::Result InitializeTextureContents(const gl::Context *context, ...@@ -121,16 +153,19 @@ angle::Result InitializeTextureContents(const gl::Context *context,
auto mtlRowRegion = MTLRegionMake2D(0, 0, size.width, 1); auto mtlRowRegion = MTLRegionMake2D(0, 0, size.width, 1);
for (NSUInteger d = 0; d < static_cast<NSUInteger>(size.depth); ++d)
{
mtlRowRegion.origin.z = d + startDepth;
for (NSUInteger r = 0; r < static_cast<NSUInteger>(size.height); ++r) for (NSUInteger r = 0; r < static_cast<NSUInteger>(size.height); ++r)
{ {
mtlRowRegion.origin.y = r; mtlRowRegion.origin.y = r;
// Upload to texture // Upload to texture
texture->replaceRegion(contextMtl, mtlRowRegion, index.getLevelIndex(), texture->replace2DRegion(contextMtl, mtlRowRegion, index.getLevelIndex(), layer,
index.hasLayer() ? index.cubeMapFaceIndex() : 0,
conversionRow.data(), dstRowPitch); conversionRow.data(), dstRowPitch);
} }
} }
}
else else
{ {
ANGLE_TRY(InitializeTextureContentsGPU(context, texture, textureObjFormat, index, ANGLE_TRY(InitializeTextureContentsGPU(context, texture, textureObjFormat, index,
...@@ -147,12 +182,11 @@ angle::Result InitializeTextureContentsGPU(const gl::Context *context, ...@@ -147,12 +182,11 @@ angle::Result InitializeTextureContentsGPU(const gl::Context *context,
MTLColorWriteMask channelsToInit) MTLColorWriteMask channelsToInit)
{ {
ContextMtl *contextMtl = mtl::GetImpl(context); ContextMtl *contextMtl = mtl::GetImpl(context);
// NOTE(hqle): Support layered textures GLint sliceOrDepth = GetSliceOrDepth(index);
ASSERT(!index.hasLayer());
// Use clear render command // Use clear render command
RenderTargetMtl tempRtt; RenderTargetMtl tempRtt;
tempRtt.set(texture, index.getLevelIndex(), 0, textureObjFormat); tempRtt.set(texture, index.getLevelIndex(), sliceOrDepth, textureObjFormat);
// temporarily enable color channels requested via channelsToInit. Some emulated format has some // temporarily enable color channels requested via channelsToInit. Some emulated format has some
// channels write mask disabled when the texture is created. // channels write mask disabled when the texture is created.
...@@ -189,6 +223,39 @@ angle::Result InitializeTextureContentsGPU(const gl::Context *context, ...@@ -189,6 +223,39 @@ angle::Result InitializeTextureContentsGPU(const gl::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result ReadTexturePerSliceBytes(const gl::Context *context,
const TextureRef &texture,
size_t bytesPerRow,
const gl::Rectangle &fromRegion,
uint32_t mipLevel,
uint32_t sliceOrDepth,
uint8_t *dataOut)
{
ASSERT(texture && texture->valid());
ContextMtl *contextMtl = mtl::GetImpl(context);
GLint layer = 0;
GLint startDepth = 0;
switch (texture->textureType())
{
case MTLTextureTypeCube:
case MTLTextureType2DArray:
layer = sliceOrDepth;
break;
case MTLTextureType3D:
startDepth = sliceOrDepth;
break;
default:
break;
}
MTLRegion mtlRegion = MTLRegionMake3D(fromRegion.x, fromRegion.y, startDepth, fromRegion.width,
fromRegion.height, 1);
texture->getBytes(contextMtl, bytesPerRow, 0, mtlRegion, mipLevel, layer, dataOut);
return angle::Result::Continue;
}
MTLViewport GetViewport(const gl::Rectangle &rect, double znear, double zfar) MTLViewport GetViewport(const gl::Rectangle &rect, double znear, double zfar)
{ {
MTLViewport re; MTLViewport re;
......
...@@ -506,6 +506,65 @@ TEST_P(CopyTexImageTest, DeleteAfterCopyingToTextures) ...@@ -506,6 +506,65 @@ TEST_P(CopyTexImageTest, DeleteAfterCopyingToTextures)
// Crashes on Intel GPUs on macOS. // Crashes on Intel GPUs on macOS.
texture2.reset(); texture2.reset();
} }
// Test if glCopyTexImage2D() implementation performs conversions well from GL_TEXTURE_3D to
// GL_TEXTURE_2D.
// This is similar to CopyTexImageTestES3.CopyTexSubImageFromTexture3D but for GL_OES_texture_3D
// extension.
TEST_P(CopyTexImageTest, CopyTexSubImageFrom3DTexureOES)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_texture_3D"));
// TODO(anglebug.com/3801)
// Seems to fail on D3D11 Windows.
ANGLE_SKIP_TEST_IF(IsD3D11() & IsWindows());
// http://anglebug.com/4927
ANGLE_SKIP_TEST_IF(IsPixel2() || IsOpenGLES());
constexpr GLsizei kDepth = 6;
// The framebuffer will be a slice of a 3d texture with a different colors for each slice. Each
// glCopyTexSubImage2D will take one face of this image to copy over a pixel in a 1x6
// framebuffer.
GLColor fboPixels[kDepth] = {GLColor::red, GLColor::yellow, GLColor::green,
GLColor::cyan, GLColor::blue, GLColor::magenta};
GLColor whitePixels[kDepth] = {GLColor::white, GLColor::white, GLColor::white,
GLColor::white, GLColor::white, GLColor::white};
GLTexture fboTex;
glBindTexture(GL_TEXTURE_3D, fboTex);
glTexImage3DOES(GL_TEXTURE_3D, 0, GL_RGBA, 1, 1, kDepth, 0, GL_RGBA, GL_UNSIGNED_BYTE,
fboPixels);
GLTexture dstTex;
glBindTexture(GL_TEXTURE_2D, dstTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kDepth, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, whitePixels);
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
for (GLsizei slice = 0; slice < kDepth; ++slice)
{
glFramebufferTexture3DOES(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_3D, fboTex, 0,
slice);
ASSERT_GL_NO_ERROR();
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
// Copy the fbo (a 3d slice) into a pixel of the destination texture.
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, slice, 0, 0, 0, 1, 1);
}
// Make sure all the copies are done correctly.
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dstTex, 0);
ASSERT_GL_NO_ERROR();
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
for (GLsizei slice = 0; slice < kDepth; ++slice)
{
EXPECT_PIXEL_COLOR_EQ(slice, 0, fboPixels[slice]);
}
}
// specialization of CopyTexImageTest is added so that some tests can be explicitly run with an ES3 // specialization of CopyTexImageTest is added so that some tests can be explicitly run with an ES3
// context // context
......
...@@ -206,6 +206,15 @@ class DrawBuffersTest : public ANGLETest ...@@ -206,6 +206,15 @@ class DrawBuffersTest : public ANGLETest
verifyAttachment2DColor(index, texture, target, level, getColorForIndex(index)); verifyAttachment2DColor(index, texture, target, level, getColorForIndex(index));
} }
void verifyAttachment3DOES(unsigned int index, GLuint texture, GLint level, GLint layer)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_texture_3D"));
glFramebufferTexture3DOES(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_3D, texture,
level, layer);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, getColorForIndex(index));
}
void verifyAttachmentLayer(unsigned int index, GLuint texture, GLint level, GLint layer) void verifyAttachmentLayer(unsigned int index, GLuint texture, GLint level, GLint layer)
{ {
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, level, layer); glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, level, layer);
...@@ -597,6 +606,53 @@ TEST_P(DrawBuffersTest, BroadcastGLFragColor) ...@@ -597,6 +606,53 @@ TEST_P(DrawBuffersTest, BroadcastGLFragColor)
glDeleteProgram(program); glDeleteProgram(program);
} }
// Test that binding multiple layers of a 3D texture works correctly.
// This is the same as DrawBuffersTestES3.3DTextures but is used for GL_OES_texture_3D extension
// on GLES 2.0 instead.
TEST_P(DrawBuffersTest, 3DTexturesOES)
{
ANGLE_SKIP_TEST_IF(!setupTest());
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_texture_3D"));
GLTexture texture;
glBindTexture(GL_TEXTURE_3D, texture.get());
glTexImage3DOES(GL_TEXTURE_3D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(),
getWindowWidth(), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture3DOES(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_3D, texture.get(), 0,
0);
glFramebufferTexture3DOES(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_3D, texture.get(), 0,
1);
glFramebufferTexture3DOES(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_3D, texture.get(), 0,
2);
glFramebufferTexture3DOES(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_3D, texture.get(), 0,
3);
bool flags[8] = {true, true, true, true, false};
GLuint program;
setupMRTProgram(flags, &program);
const GLenum bufs[] = {
GL_COLOR_ATTACHMENT0,
GL_COLOR_ATTACHMENT1,
GL_COLOR_ATTACHMENT2,
GL_COLOR_ATTACHMENT3,
};
setDrawBuffers(4, bufs);
drawQuad(program, positionAttrib(), 0.5);
verifyAttachment3DOES(0, texture.get(), 0, 0);
verifyAttachment3DOES(1, texture.get(), 0, 1);
verifyAttachment3DOES(2, texture.get(), 0, 2);
verifyAttachment3DOES(3, texture.get(), 0, 3);
EXPECT_GL_NO_ERROR();
glDeleteProgram(program);
}
class DrawBuffersTestES3 : public DrawBuffersTest class DrawBuffersTestES3 : public DrawBuffersTest
{}; {};
......
...@@ -58,6 +58,7 @@ class MipmapTest : public BaseMipmapTest ...@@ -58,6 +58,7 @@ class MipmapTest : public BaseMipmapTest
mCubeProgram(0), mCubeProgram(0),
mTexture2D(0), mTexture2D(0),
mTextureCube(0), mTextureCube(0),
m3DProgram(0),
mLevelZeroBlueInitData(), mLevelZeroBlueInitData(),
mLevelZeroWhiteInitData(), mLevelZeroWhiteInitData(),
mLevelOneGreenInitData(), mLevelOneGreenInitData(),
...@@ -123,12 +124,62 @@ void main() ...@@ -123,12 +124,62 @@ void main()
ASSERT_NE(0u, mCubeProgram); ASSERT_NE(0u, mCubeProgram);
} }
void setUp3DProgram()
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_texture_3D"));
// http://anglebug.com/4927
ANGLE_SKIP_TEST_IF(IsPixel2() || IsOpenGLES());
// Vertex Shader source
constexpr char kVS[] = R"(attribute vec4 position;
varying vec2 vTexCoord;
void main()
{
gl_Position = position;
vTexCoord = (position.xy * 0.5) + 0.5;
})";
constexpr char kFS[] = R"(#version 100
#extension GL_OES_texture_3D : enable
precision highp float;
uniform highp sampler3D tex;
uniform float slice;
uniform float lod;
varying vec2 vTexCoord;
void main()
{
gl_FragColor = texture3DLod(tex, vec3(vTexCoord, slice), lod);
})";
m3DProgram = CompileProgram(kVS, kFS);
if (m3DProgram == 0)
{
FAIL() << "shader compilation failed.";
}
mTexture3DSliceUniformLocation = glGetUniformLocation(m3DProgram, "slice");
ASSERT_NE(-1, mTexture3DSliceUniformLocation);
mTexture3DLODUniformLocation = glGetUniformLocation(m3DProgram, "lod");
ASSERT_NE(-1, mTexture3DLODUniformLocation);
glUseProgram(m3DProgram);
glUniform1f(mTexture3DLODUniformLocation, 0);
glUseProgram(0);
ASSERT_GL_NO_ERROR();
}
void testSetUp() override void testSetUp() override
{ {
setUp2DProgram(); setUp2DProgram();
setUpCubeProgram(); setUpCubeProgram();
setUp3DProgram();
mLevelZeroBlueInitData = mLevelZeroBlueInitData =
createRGBInitData(getWindowWidth(), getWindowHeight(), 0, 0, 255); // Blue createRGBInitData(getWindowWidth(), getWindowHeight(), 0, 0, 255); // Blue
mLevelZeroWhiteInitData = mLevelZeroWhiteInitData =
...@@ -168,6 +219,7 @@ void main() ...@@ -168,6 +219,7 @@ void main()
{ {
glDeleteProgram(m2DProgram); glDeleteProgram(m2DProgram);
glDeleteProgram(mCubeProgram); glDeleteProgram(mCubeProgram);
glDeleteProgram(m3DProgram);
glDeleteFramebuffers(1, &mOffscreenFramebuffer); glDeleteFramebuffers(1, &mOffscreenFramebuffer);
glDeleteTextures(1, &mTexture2D); glDeleteTextures(1, &mTexture2D);
glDeleteTextures(1, &mTextureCube); glDeleteTextures(1, &mTextureCube);
...@@ -207,6 +259,10 @@ void main() ...@@ -207,6 +259,10 @@ void main()
GLuint mTexture2D; GLuint mTexture2D;
GLuint mTextureCube; GLuint mTextureCube;
GLuint m3DProgram = 0;
GLint mTexture3DSliceUniformLocation;
GLint mTexture3DLODUniformLocation;
std::vector<GLubyte> mLevelZeroBlueInitData; std::vector<GLubyte> mLevelZeroBlueInitData;
std::vector<GLubyte> mLevelZeroWhiteInitData; std::vector<GLubyte> mLevelZeroWhiteInitData;
std::vector<GLubyte> mLevelOneGreenInitData; std::vector<GLubyte> mLevelOneGreenInitData;
...@@ -1055,6 +1111,99 @@ TEST_P(MipmapTest, TextureCubeRenderToLevelZero) ...@@ -1055,6 +1111,99 @@ TEST_P(MipmapTest, TextureCubeRenderToLevelZero)
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
} }
// Creates a mipmapped 3D texture with two layers, and calls ANGLE's GenerateMipmap.
// Then tests if the mipmaps are rendered correctly for all two layers.
// This is the same as MipmapTestES3.MipmapsForTexture3D but for GL_OES_texture_3D extension on
// GLES 2.0 instead.
TEST_P(MipmapTest, MipmapsForTexture3DOES)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_texture_3D"));
// http://anglebug.com/4927
ANGLE_SKIP_TEST_IF(IsPixel2() || IsOpenGLES());
int px = getWindowWidth() / 2;
int py = getWindowHeight() / 2;
GLTexture texture;
glBindTexture(GL_TEXTURE_3D, texture);
glTexImage3DOES(GL_TEXTURE_3D, 0, GL_RGBA, 16, 16, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexImage3DOES(GL_TEXTURE_3D, 1, GL_RGBA, 8, 8, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexImage3DOES(GL_TEXTURE_3D, 2, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexImage3DOES(GL_TEXTURE_3D, 3, GL_RGBA, 2, 2, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexImage3DOES(GL_TEXTURE_3D, 4, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
// Fill the first layer with red
std::vector<GLColor> pixelsRed(16 * 16, GLColor::red);
glTexSubImage3DOES(GL_TEXTURE_3D, 0, 0, 0, 0, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE,
pixelsRed.data());
// Fill the second layer with green
std::vector<GLColor> pixelsGreen(16 * 16, GLColor::green);
glTexSubImage3DOES(GL_TEXTURE_3D, 0, 0, 0, 1, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE,
pixelsGreen.data());
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
EXPECT_GL_NO_ERROR();
glGenerateMipmap(GL_TEXTURE_3D);
EXPECT_GL_NO_ERROR();
glUseProgram(m3DProgram);
EXPECT_GL_NO_ERROR();
// Mipmap level 0
// Draw the first slice
glUniform1f(mTexture3DLODUniformLocation, 0.);
glUniform1f(mTexture3DSliceUniformLocation, 0.25f);
drawQuad(m3DProgram, "position", 0.5f);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
// Draw the second slice
glUniform1f(mTexture3DSliceUniformLocation, 0.75f);
drawQuad(m3DProgram, "position", 0.5f);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::green);
// Regenerate mipmap of same color texture
glTexSubImage3DOES(GL_TEXTURE_3D, 0, 0, 0, 1, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE,
pixelsRed.data());
glGenerateMipmap(GL_TEXTURE_3D);
EXPECT_GL_NO_ERROR();
// Mipmap level 1 8*8*1
glUniform1f(mTexture3DLODUniformLocation, 1.);
drawQuad(m3DProgram, "position", 0.5f);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
// Mipmap level 2 4*4*1
glUniform1f(mTexture3DLODUniformLocation, 2.);
drawQuad(m3DProgram, "position", 0.5f);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
// Mipmap level 3 2*2*1
glUniform1f(mTexture3DLODUniformLocation, 3.);
drawQuad(m3DProgram, "position", 0.5f);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
// Mipmap level 4 1*1*1
glUniform1f(mTexture3DLODUniformLocation, 4.);
drawQuad(m3DProgram, "position", 0.5f);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
}
// Creates a mipmapped 2D array texture with three layers, and calls ANGLE's GenerateMipmap. // Creates a mipmapped 2D array texture with three layers, and calls ANGLE's GenerateMipmap.
// Then tests if the mipmaps are rendered correctly for all three layers. // Then tests if the mipmaps are rendered correctly for all three layers.
TEST_P(MipmapTestES3, MipmapsForTextureArray) TEST_P(MipmapTestES3, MipmapsForTextureArray)
......
...@@ -1074,16 +1074,17 @@ class TextureSizeTextureArrayTest : public TexCoordDrawTest ...@@ -1074,16 +1074,17 @@ class TextureSizeTextureArrayTest : public TexCoordDrawTest
GLint mTexture1Location; GLint mTexture1Location;
}; };
class Texture3DTestES3 : public TexCoordDrawTest // Test for GL_OES_texture_3D extension
class Texture3DTestES2 : public TexCoordDrawTest
{ {
protected: protected:
Texture3DTestES3() : TexCoordDrawTest(), mTexture3D(0), mTexture3DUniformLocation(-1) {} Texture3DTestES2() : TexCoordDrawTest(), mTexture3D(0), mTexture3DUniformLocation(-1) {}
const char *getVertexShaderSource() override const char *getVertexShaderSource() override
{ {
return "#version 300 es\n" return "#version 100\n"
"out vec2 texcoord;\n" "varying vec2 texcoord;\n"
"in vec4 position;\n" "attribute vec4 position;\n"
"void main()\n" "void main()\n"
"{\n" "{\n"
" gl_Position = vec4(position.xy, 0.0, 1.0);\n" " gl_Position = vec4(position.xy, 0.0, 1.0);\n"
...@@ -1093,14 +1094,25 @@ class Texture3DTestES3 : public TexCoordDrawTest ...@@ -1093,14 +1094,25 @@ class Texture3DTestES3 : public TexCoordDrawTest
const char *getFragmentShaderSource() override const char *getFragmentShaderSource() override
{ {
return "#version 300 es\n" if (!hasTexture3DExt())
{
return "#version 100\n"
"precision highp float;\n"
"varying vec2 texcoord;\n"
"void main()\n"
"{\n"
" gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
"}\n";
}
return "#version 100\n"
"#extension GL_OES_texture_3D : enable\n"
"precision highp float;\n" "precision highp float;\n"
"uniform highp sampler3D tex3D;\n" "uniform highp sampler3D tex3D;\n"
"in vec2 texcoord;\n" "uniform highp float level;\n"
"out vec4 fragColor;\n" "varying vec2 texcoord;\n"
"void main()\n" "void main()\n"
"{\n" "{\n"
" fragColor = texture(tex3D, vec3(texcoord, 0.0));\n" " gl_FragColor = texture3DLod(tex3D, vec3(texcoord, 0.0), level);\n"
"}\n"; "}\n";
} }
...@@ -1113,8 +1125,11 @@ class Texture3DTestES3 : public TexCoordDrawTest ...@@ -1113,8 +1125,11 @@ class Texture3DTestES3 : public TexCoordDrawTest
setUpProgram(); setUpProgram();
mTexture3DUniformLocation = glGetUniformLocation(mProgram, "tex3D"); mTexture3DUniformLocation = glGetUniformLocation(mProgram, "tex3D");
if (hasTexture3DExt())
{
ASSERT_NE(-1, mTexture3DUniformLocation); ASSERT_NE(-1, mTexture3DUniformLocation);
} }
}
void testTearDown() override void testTearDown() override
{ {
...@@ -1122,10 +1137,51 @@ class Texture3DTestES3 : public TexCoordDrawTest ...@@ -1122,10 +1137,51 @@ class Texture3DTestES3 : public TexCoordDrawTest
TexCoordDrawTest::testTearDown(); TexCoordDrawTest::testTearDown();
} }
bool hasTexture3DExt() const
{
// http://anglebug.com/4927
if (IsPixel2() || IsOpenGLES())
{
return false;
}
return IsGLExtensionEnabled("GL_OES_texture_3D");
}
GLuint mTexture3D; GLuint mTexture3D;
GLint mTexture3DUniformLocation; GLint mTexture3DUniformLocation;
}; };
class Texture3DTestES3 : public Texture3DTestES2
{
protected:
Texture3DTestES3() : Texture3DTestES2() {}
const char *getVertexShaderSource() override
{
return "#version 300 es\n"
"out vec2 texcoord;\n"
"in vec4 position;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(position.xy, 0.0, 1.0);\n"
" texcoord = (position.xy * 0.5) + 0.5;\n"
"}\n";
}
const char *getFragmentShaderSource() override
{
return "#version 300 es\n"
"precision highp float;\n"
"uniform highp sampler3D tex3D;\n"
"in vec2 texcoord;\n"
"out vec4 fragColor;\n"
"void main()\n"
"{\n"
" fragColor = texture(tex3D, vec3(texcoord, 0.0));\n"
"}\n";
}
};
class ShadowSamplerPlusSampler3DTestES3 : public TexCoordDrawTest class ShadowSamplerPlusSampler3DTestES3 : public TexCoordDrawTest
{ {
protected: protected:
...@@ -2422,6 +2478,146 @@ TEST_P(Texture2DTest, NPOTSubImageParameters) ...@@ -2422,6 +2478,146 @@ TEST_P(Texture2DTest, NPOTSubImageParameters)
EXPECT_GL_NO_ERROR(); EXPECT_GL_NO_ERROR();
} }
// Test that drawing works correctly RGBA 3D texture
TEST_P(Texture3DTestES2, RGBA)
{
ANGLE_SKIP_TEST_IF(!hasTexture3DExt());
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, mTexture3D);
std::vector<GLColor> texDataGreen(2u * 2u * 2u, GLColor::green);
std::vector<GLColor> texDataRed(1u * 1u * 1u, GLColor::red);
glTexImage3DOES(GL_TEXTURE_3D, 0, GL_RGBA, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
texDataGreen.data());
glTexImage3DOES(GL_TEXTURE_3D, 1, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
texDataRed.data());
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
EXPECT_GL_NO_ERROR();
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Test that drawing works correctly Luminance 3D texture
TEST_P(Texture3DTestES2, Luminance)
{
ANGLE_SKIP_TEST_IF(!hasTexture3DExt());
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, mTexture3D);
std::vector<GLubyte> texData(2u * 2u * 2u, 125);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage3DOES(GL_TEXTURE_3D, 0, GL_LUMINANCE, 2, 2, 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
texData.data());
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
EXPECT_GL_NO_ERROR();
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor(125, 125, 125, 255));
}
// Test that drawing works correctly with glCopyTexSubImage3D
TEST_P(Texture3DTestES2, CopySubImageRGBA)
{
ANGLE_SKIP_TEST_IF(!hasTexture3DExt());
glClearColor(0, 0, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, mTexture3D);
std::vector<GLColor> texDataRed(4u * 4u * 4u, GLColor::red);
glTexImage3DOES(GL_TEXTURE_3D, 0, GL_RGBA, 4, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE,
texDataRed.data());
glTexImage3DOES(GL_TEXTURE_3D, 1, GL_RGBA, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
texDataRed.data());
glTexImage3DOES(GL_TEXTURE_3D, 2, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
texDataRed.data());
glCopyTexSubImage3DOES(GL_TEXTURE_3D, 1, 0, 0, 0, 0, 0, 2, 2);
glCopyTexSubImage3DOES(GL_TEXTURE_3D, 1, 0, 0, 1, 0, 0, 2, 2);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
EXPECT_GL_NO_ERROR();
glClearColor(0, 1, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
glUseProgram(mProgram);
glUniform1f(glGetUniformLocation(mProgram, "level"), 1);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
}
TEST_P(Texture3DTestES2, CopySubImageLuminance)
{
ANGLE_SKIP_TEST_IF(!hasTexture3DExt());
glClearColor(1, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, mTexture3D);
glTexImage3DOES(GL_TEXTURE_3D, 0, GL_LUMINANCE, 4, 4, 4, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
nullptr);
glTexImage3DOES(GL_TEXTURE_3D, 1, GL_LUMINANCE, 2, 2, 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
nullptr);
glTexImage3DOES(GL_TEXTURE_3D, 2, GL_LUMINANCE, 1, 1, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
nullptr);
glCopyTexSubImage3DOES(GL_TEXTURE_3D, 1, 0, 0, 0, 0, 0, 2, 2);
glCopyTexSubImage3DOES(GL_TEXTURE_3D, 1, 0, 0, 1, 0, 0, 2, 2);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
EXPECT_GL_NO_ERROR();
glClearColor(0, 1, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
glUseProgram(mProgram);
glUniform1f(glGetUniformLocation(mProgram, "level"), 1);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
}
TEST_P(Texture3DTestES2, CopySubImageAlpha)
{
ANGLE_SKIP_TEST_IF(!hasTexture3DExt());
glClearColor(1, 0, 0, 0.5);
glClear(GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, mTexture3D);
glTexImage3DOES(GL_TEXTURE_3D, 0, GL_ALPHA, 4, 4, 4, 0, GL_ALPHA, GL_UNSIGNED_BYTE, nullptr);
glTexImage3DOES(GL_TEXTURE_3D, 1, GL_ALPHA, 2, 2, 2, 0, GL_ALPHA, GL_UNSIGNED_BYTE, nullptr);
glTexImage3DOES(GL_TEXTURE_3D, 2, GL_ALPHA, 1, 1, 1, 0, GL_ALPHA, GL_UNSIGNED_BYTE, nullptr);
glCopyTexSubImage3DOES(GL_TEXTURE_3D, 1, 0, 0, 0, 0, 0, 2, 2);
glCopyTexSubImage3DOES(GL_TEXTURE_3D, 1, 0, 0, 1, 0, 0, 2, 2);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
EXPECT_GL_NO_ERROR();
glClearColor(0, 1, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
glUseProgram(mProgram);
glUniform1f(glGetUniformLocation(mProgram, "level"), 1);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(0, 0, 0, 128), 1.0);
}
// Regression test for http://crbug.com/949985 to make sure dirty bits are propagated up from // Regression test for http://crbug.com/949985 to make sure dirty bits are propagated up from
// TextureImpl and the texture is synced before being used in a draw call. // TextureImpl and the texture is synced before being used in a draw call.
TEST_P(Texture2DTestES3, TextureImplPropogatesDirtyBits) TEST_P(Texture2DTestES3, TextureImplPropogatesDirtyBits)
...@@ -7340,6 +7536,7 @@ ANGLE_INSTANTIATE_TEST_ES2(SamplerArrayAsFunctionParameterTest); ...@@ -7340,6 +7536,7 @@ ANGLE_INSTANTIATE_TEST_ES2(SamplerArrayAsFunctionParameterTest);
ANGLE_INSTANTIATE_TEST_ES3(Texture2DTestES3); ANGLE_INSTANTIATE_TEST_ES3(Texture2DTestES3);
ANGLE_INSTANTIATE_TEST_ES31(Texture2DTestES31PPO); ANGLE_INSTANTIATE_TEST_ES31(Texture2DTestES31PPO);
ANGLE_INSTANTIATE_TEST_ES3(Texture2DBaseMaxTestES3); ANGLE_INSTANTIATE_TEST_ES3(Texture2DBaseMaxTestES3);
ANGLE_INSTANTIATE_TEST_ES2(Texture3DTestES2);
ANGLE_INSTANTIATE_TEST_ES3(Texture3DTestES3); ANGLE_INSTANTIATE_TEST_ES3(Texture3DTestES3);
ANGLE_INSTANTIATE_TEST_ES3(Texture2DIntegerAlpha1TestES3); ANGLE_INSTANTIATE_TEST_ES3(Texture2DIntegerAlpha1TestES3);
ANGLE_INSTANTIATE_TEST_ES3(Texture2DUnsignedIntegerAlpha1TestES3); ANGLE_INSTANTIATE_TEST_ES3(Texture2DUnsignedIntegerAlpha1TestES3);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment