Commit bdecaf33 by Le Hoang Quyen Committed by Commit Bot

Metal: Implement PBO.

Bug: angleproject:2634 Change-Id: I77f085227298bf46361825d1886e04830dc9987a Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2336558 Commit-Queue: Le Hoang Quyen <le.hoang.q@gmail.com> Reviewed-by: 's avatarJonah Ryan-Davis <jonahr@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 7b7e52fa
......@@ -28,7 +28,7 @@ namespace rx
// Conversion buffers hold translated index and vertex data.
struct ConversionBufferMtl
{
ConversionBufferMtl(const gl::Context *context, size_t initialSize, size_t alignment);
ConversionBufferMtl(ContextMtl *context, size_t initialSize, size_t alignment);
~ConversionBufferMtl();
// One state value determines if we need to re-stream vertex data.
......@@ -42,13 +42,24 @@ struct ConversionBufferMtl
size_t convertedOffset;
};
struct IndexConversionBufferMtl : public ConversionBufferMtl
struct VertexConversionBufferMtl : public ConversionBufferMtl
{
IndexConversionBufferMtl(const gl::Context *context,
gl::DrawElementsType type,
VertexConversionBufferMtl(ContextMtl *context,
angle::FormatID formatIDIn,
GLuint strideIn,
size_t offsetIn);
const gl::DrawElementsType type;
// The conversion is identified by the triple of {format, stride, offset}.
angle::FormatID formatID;
GLuint stride;
size_t offset;
};
struct IndexConversionBufferMtl : public ConversionBufferMtl
{
IndexConversionBufferMtl(ContextMtl *context, gl::DrawElementsType elemType, size_t offsetIn);
const gl::DrawElementsType elemType;
const size_t offset;
};
......@@ -108,25 +119,32 @@ class BufferMtl : public BufferImpl, public BufferHolderMtl
bool primitiveRestartEnabled,
gl::IndexRange *outRange) override;
angle::Result getFirstLastIndices(gl::DrawElementsType type,
void onDataChanged() override;
angle::Result getFirstLastIndices(ContextMtl *contextMtl,
gl::DrawElementsType type,
size_t offset,
size_t count,
std::pair<uint32_t, uint32_t> *outIndices) const;
std::pair<uint32_t, uint32_t> *outIndices);
const uint8_t *getClientShadowCopyData(const gl::Context *context);
const uint8_t *getClientShadowCopyData(ContextMtl *contextMtl);
ConversionBufferMtl *getVertexConversionBuffer(const gl::Context *context,
ConversionBufferMtl *getVertexConversionBuffer(ContextMtl *context,
angle::FormatID formatID,
GLuint stride,
size_t offset);
IndexConversionBufferMtl *getIndexConversionBuffer(const gl::Context *context,
gl::DrawElementsType type,
IndexConversionBufferMtl *getIndexConversionBuffer(ContextMtl *context,
gl::DrawElementsType elemType,
size_t offset);
size_t size() const { return static_cast<size_t>(mState.getSize()); }
private:
angle::Result setDataImpl(const gl::Context *context,
const void *data,
size_t size,
gl::BufferUsage usage);
angle::Result setSubDataImpl(const gl::Context *context,
const void *data,
size_t size,
......@@ -138,6 +156,9 @@ class BufferMtl : public BufferImpl, public BufferHolderMtl
void markConversionBuffersDirty();
void clearConversionBuffers();
bool clientShadowCopyDataNeedSync(ContextMtl *contextMtl);
void ensureShadowCopySyncedFromGPU(ContextMtl *contextMtl);
uint8_t *syncAndObtainShadowCopy(ContextMtl *contextMtl);
// Client side shadow buffer
angle::MemoryBuffer mShadowCopy;
......@@ -145,21 +166,8 @@ class BufferMtl : public BufferImpl, public BufferHolderMtl
// GPU side buffers pool
mtl::BufferPool mBufferPool;
struct VertexConversionBuffer : public ConversionBufferMtl
{
VertexConversionBuffer(const gl::Context *context,
angle::FormatID formatIDIn,
GLuint strideIn,
size_t offsetIn);
// The conversion is identified by the triple of {format, stride, offset}.
angle::FormatID formatID;
GLuint stride;
size_t offset;
};
// A cache of converted vertex data.
std::vector<VertexConversionBuffer> mVertexConversionBuffers;
std::vector<VertexConversionBufferMtl> mVertexConversionBuffers;
std::vector<IndexConversionBufferMtl> mIndexConversionBuffers;
};
......
......@@ -588,7 +588,7 @@ void DisplayMtl::initializeExtensions() const
// Enable this for simple buffer readback testing, but some functionality is missing.
// NOTE(hqle): Support full mapBufferRange extension.
mNativeExtensions.mapBufferOES = true;
mNativeExtensions.mapBufferRange = false;
mNativeExtensions.mapBufferRange = true;
mNativeExtensions.textureStorage = true;
mNativeExtensions.drawBuffers = true;
mNativeExtensions.fragDepth = true;
......@@ -646,6 +646,9 @@ void DisplayMtl::initializeExtensions() const
// GL_APPLE_clip_distance
mNativeExtensions.clipDistanceAPPLE = true;
// GL_NV_pixel_buffer_object
mNativeExtensions.pixelBufferObjectNV = true;
}
void DisplayMtl::initializeTextureCaps() const
......
......@@ -172,6 +172,11 @@ class FramebufferMtl : public FramebufferImpl
const gl::FramebufferAttachment *attachment,
RenderTargetMtl **cachedRenderTarget);
angle::Result readPixelsToPBO(const gl::Context *context,
const gl::Rectangle &area,
const PackPixelsParams &packPixelsParams,
RenderTargetMtl *renderTarget);
// NOTE: we cannot use RenderTargetCache here because it doesn't support separate
// depth & stencil attachments as of now. Separate depth & stencil could be useful to
// save spaces on iOS devices. See doc/PackedDepthStencilSupport.md.
......
......@@ -1068,16 +1068,16 @@ angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context,
uint8_t *pixels)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
if (packPixelsParams.packBuffer)
{
// NOTE(hqle): PBO is not supported atm
ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_OPERATION);
}
if (!renderTarget)
{
return angle::Result::Continue;
}
if (packPixelsParams.packBuffer)
{
return readPixelsToPBO(context, area, packPixelsParams, renderTarget);
}
mtl::TextureRef texture;
if (mBackbuffer)
{
......@@ -1127,4 +1127,114 @@ angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context,
return angle::Result::Continue;
}
angle::Result FramebufferMtl::readPixelsToPBO(const gl::Context *context,
const gl::Rectangle &area,
const PackPixelsParams &packPixelsParams,
RenderTargetMtl *renderTarget)
{
ASSERT(packPixelsParams.packBuffer);
ASSERT(renderTarget);
ContextMtl *contextMtl = mtl::GetImpl(context);
ANGLE_MTL_CHECK(contextMtl, packPixelsParams.offset <= std::numeric_limits<uint32_t>::max(),
GL_INVALID_OPERATION);
const uint32_t dstBufferOffset = static_cast<uint32_t>(packPixelsParams.offset);
const uint32_t dstBufferRowPitch = packPixelsParams.outputPitch;
const angle::Format &dstAngleFormat = *packPixelsParams.destFormat;
const bool reverseRowOrder = packPixelsParams.reverseRowOrder;
BufferMtl *packBufferMtl = mtl::GetImpl(packPixelsParams.packBuffer);
mtl::BufferRef dstBuffer = packBufferMtl->getCurrentBuffer();
const mtl::Format &readFormat = *renderTarget->getFormat();
const angle::Format &readAngleFormat = readFormat.actualAngleFormat();
mtl::TextureRef texture = renderTarget->getTexture();
if (dstAngleFormat.id != readAngleFormat.id || texture->samples() > 1 ||
(dstBufferOffset % dstAngleFormat.pixelBytes) ||
(dstBufferOffset % mtl::kTextureToBufferBlittingAlignment))
{
const angle::Format *actualDstAngleFormat;
// SRGB is special case: We need to write sRGB values to buffer, not linear values.
switch (readAngleFormat.id)
{
case angle::FormatID::B8G8R8A8_UNORM_SRGB:
case angle::FormatID::R8G8B8_UNORM_SRGB:
case angle::FormatID::R8G8B8A8_UNORM_SRGB:
if (dstAngleFormat.id != readAngleFormat.id)
{
switch (dstAngleFormat.id)
{
case angle::FormatID::B8G8R8A8_UNORM:
actualDstAngleFormat =
&angle::Format::Get(angle::FormatID::B8G8R8A8_UNORM_SRGB);
break;
case angle::FormatID::R8G8B8A8_UNORM:
actualDstAngleFormat =
&angle::Format::Get(angle::FormatID::R8G8B8A8_UNORM_SRGB);
break;
default:
// Unsupported format.
ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_ENUM);
}
break;
}
OS_FALLTHROUGH;
default:
actualDstAngleFormat = &dstAngleFormat;
}
// Use compute shader
mtl::CopyPixelsToBufferParams params;
params.buffer = dstBuffer;
params.bufferStartOffset = dstBufferOffset;
params.bufferRowPitch = dstBufferRowPitch;
params.texture = texture;
params.textureArea = area;
params.textureLevel = renderTarget->getLevelIndex();
params.textureSliceOrDeph = renderTarget->getLayerIndex();
params.reverseTextureRowOrder = reverseRowOrder;
ANGLE_TRY(contextMtl->getDisplay()->getUtils().packPixelsFromTextureToBuffer(
contextMtl, *actualDstAngleFormat, params));
}
else
{
// Use blit command encoder
if (!reverseRowOrder)
{
ANGLE_TRY(mtl::ReadTexturePerSliceBytesToBuffer(
context, texture, dstBufferRowPitch, area, renderTarget->getLevelIndex(),
renderTarget->getLayerIndex(), dstBufferOffset, dstBuffer));
}
else
{
gl::Rectangle srcRowRegion(area.x, area.y, area.width, 1);
int startRow = area.y1() - 1;
uint32_t bufferRowOffset = dstBufferOffset;
// Copy pixels row by row
for (int r = startRow, copiedRows = 0; copiedRows < area.height;
++copiedRows, --r, bufferRowOffset += dstBufferRowPitch)
{
srcRowRegion.y = r;
// Read the pixels data to the buffer's row
ANGLE_TRY(mtl::ReadTexturePerSliceBytesToBuffer(
context, texture, dstBufferRowPitch, srcRowRegion,
renderTarget->getLevelIndex(), renderTarget->getLayerIndex(), bufferRowOffset,
dstBuffer));
}
}
}
return angle::Result::Continue;
}
}
......@@ -16,6 +16,7 @@
#include "common/mathutil.h"
#include "image_util/imageformats.h"
#include "libANGLE/Surface.h"
#include "libANGLE/renderer/metal/BufferMtl.h"
#include "libANGLE/renderer/metal/ContextMtl.h"
#include "libANGLE/renderer/metal/DisplayMtl.h"
#include "libANGLE/renderer/metal/FrameBufferMtl.h"
......@@ -1283,10 +1284,7 @@ angle::Result TextureMtl::setSubImageImpl(const gl::Context *context,
gl::Buffer *unpackBuffer,
const uint8_t *oriPixels)
{
// NOTE(hqle): Support PBO
ASSERT(!unpackBuffer);
if (!oriPixels)
if (!oriPixels && !unpackBuffer)
{
return angle::Result::Continue;
}
......@@ -1354,13 +1352,47 @@ angle::Result TextureMtl::setPerSliceSubImage(const gl::Context *context,
unpackBuffer, pixels, image);
}
// NOTE(hqle): Support PBO
ASSERT(!unpackBuffer);
// No conversion needed.
ContextMtl *contextMtl = mtl::GetImpl(context);
// Upload texture data directly
ANGLE_TRY(UploadTextureContents(context, mFormat.actualAngleFormat(), mtlArea, 0, slice, pixels,
pixelsRowPitch, pixelsDepthPitch, image));
if (unpackBuffer)
{
uintptr_t offset = reinterpret_cast<uintptr_t>(pixels);
if (offset % mFormat.actualAngleFormat().pixelBytes)
{
// offset is not divisible by pixelByte, use convertAndSetPerSliceSubImage() function.
return convertAndSetPerSliceSubImage(context, slice, mtlArea, internalFormat, type,
pixelsAngleFormat, pixelsRowPitch,
pixelsDepthPitch, unpackBuffer, pixels, image);
}
BufferMtl *unpackBufferMtl = mtl::GetImpl(unpackBuffer);
if (mFormat.hasDepthAndStencilBits())
{
// NOTE(hqle): packed depth & stencil texture cannot copy from buffer directly, needs
// to split its depth & stencil data and copy separately.
const uint8_t *clientData = unpackBufferMtl->getClientShadowCopyData(contextMtl);
clientData += offset;
ANGLE_TRY(UploadTextureContents(context, mFormat.actualAngleFormat(), mtlArea, 0, slice,
clientData, pixelsRowPitch, pixelsDepthPitch, image));
}
else
{
// Use blit encoder to copy
mtl::BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder();
blitEncoder->copyBufferToTexture(
unpackBufferMtl->getCurrentBuffer(), offset, pixelsRowPitch, pixelsDepthPitch,
mtlArea.size, image, slice, 0, mtlArea.origin,
mFormat.isPVRTC() ? mtl::kBlitOptionRowLinearPVRTC : MTLBlitOptionNone);
}
}
else
{
// Upload texture data directly
ANGLE_TRY(UploadTextureContents(context, mFormat.actualAngleFormat(), mtlArea, 0, slice,
pixels, pixelsRowPitch, pixelsDepthPitch, image));
}
return angle::Result::Continue;
}
......@@ -1380,11 +1412,55 @@ angle::Result TextureMtl::convertAndSetPerSliceSubImage(const gl::Context *conte
ContextMtl *contextMtl = mtl::GetImpl(context);
// NOTE(hqle): Support PBO
ASSERT(!unpackBuffer);
if (unpackBuffer)
{
ANGLE_MTL_CHECK(contextMtl,
reinterpret_cast<uintptr_t>(pixels) <= std::numeric_limits<uint32_t>::max(),
GL_INVALID_OPERATION);
uint32_t offset = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pixels));
LoadImageFunctionInfo loadFunctionInfo =
mFormat.textureLoadFunctions ? mFormat.textureLoadFunctions(type) : LoadImageFunctionInfo();
BufferMtl *unpackBufferMtl = mtl::GetImpl(unpackBuffer);
if (!mFormat.getCaps().writable || mFormat.hasDepthOrStencilBits() ||
mFormat.intendedAngleFormat().isBlock)
{
// Unsupported format, use CPU path.
const uint8_t *clientData = unpackBufferMtl->getClientShadowCopyData(contextMtl);
clientData += offset;
ANGLE_TRY(convertAndSetPerSliceSubImage(context, slice, mtlArea, internalFormat, type,
pixelsAngleFormat, pixelsRowPitch,
pixelsDepthPitch, nullptr, clientData, image));
}
else
{
// Use compute shader
mtl::CopyPixelsFromBufferParams params;
params.buffer = unpackBufferMtl->getCurrentBuffer();
params.bufferStartOffset = offset;
params.bufferRowPitch = static_cast<uint32_t>(pixelsRowPitch);
params.bufferDepthPitch = static_cast<uint32_t>(pixelsDepthPitch);
params.texture = image;
params.textureArea = mtl::MTLRegionToGLBox(mtlArea);
// If texture is not array, slice must be zero, if texture is array, mtlArea.origin.z
// must be zero.
// This is because this function uses Metal convention: where slice is only used for
// array textures, and z layer of mtlArea.origin is only used for 3D textures.
ASSERT(slice == 0 || params.textureArea.z == 0);
// For mtl::RenderUtils we convert to OpenGL convention: z layer is used as either array
// texture's slice or 3D texture's layer index.
params.textureArea.z += slice;
ANGLE_TRY(contextMtl->getDisplay()->getUtils().unpackPixelsFromBufferToTexture(
contextMtl, pixelsAngleFormat, params));
}
} // if (unpackBuffer)
else
{
LoadImageFunctionInfo loadFunctionInfo = mFormat.textureLoadFunctions
? mFormat.textureLoadFunctions(type)
: LoadImageFunctionInfo();
const angle::Format &dstFormat = angle::Format::Get(mFormat.actualFormatId);
const size_t dstRowPitch = dstFormat.pixelBytes * mtlArea.size.width;
......@@ -1396,16 +1472,18 @@ angle::Result TextureMtl::convertAndSetPerSliceSubImage(const gl::Context *conte
// Need to create a buffer to hold entire decompressed image.
const size_t dstDepthPitch = dstRowPitch * mtlArea.size.height;
angle::MemoryBuffer decompressBuf;
ANGLE_CHECK_GL_ALLOC(contextMtl, decompressBuf.resize(dstDepthPitch * mtlArea.size.depth));
ANGLE_CHECK_GL_ALLOC(contextMtl,
decompressBuf.resize(dstDepthPitch * mtlArea.size.depth));
// Decompress
loadFunctionInfo.loadFunction(mtlArea.size.width, mtlArea.size.height, mtlArea.size.depth,
pixels, pixelsRowPitch, pixelsDepthPitch,
decompressBuf.data(), dstRowPitch, dstDepthPitch);
loadFunctionInfo.loadFunction(
mtlArea.size.width, mtlArea.size.height, mtlArea.size.depth, pixels, pixelsRowPitch,
pixelsDepthPitch, decompressBuf.data(), dstRowPitch, dstDepthPitch);
// Upload to texture
ANGLE_TRY(UploadTextureContents(context, dstFormat, mtlArea, 0, slice, decompressBuf.data(),
dstRowPitch, dstDepthPitch, image));
ANGLE_TRY(UploadTextureContents(context, dstFormat, mtlArea, 0, slice,
decompressBuf.data(), dstRowPitch, dstDepthPitch,
image));
} // if (mFormat.intendedAngleFormat().isBlock)
else
{
......@@ -1427,13 +1505,13 @@ angle::Result TextureMtl::convertAndSetPerSliceSubImage(const gl::Context *conte
// Convert pixels
if (loadFunctionInfo.loadFunction)
{
loadFunctionInfo.loadFunction(mtlRow.size.width, 1, 1, psrc, pixelsRowPitch, 0,
conversionRow.data(), dstRowPitch, 0);
loadFunctionInfo.loadFunction(mtlRow.size.width, 1, 1, psrc, pixelsRowPitch,
0, conversionRow.data(), dstRowPitch, 0);
}
else if (mFormat.hasDepthOrStencilBits())
{
ConvertDepthStencilData(mtlRow.size, pixelsAngleFormat, pixelsRowPitch, 0, psrc,
dstFormat, nullptr, dstRowPitch, 0,
ConvertDepthStencilData(mtlRow.size, pixelsAngleFormat, pixelsRowPitch, 0,
psrc, dstFormat, nullptr, dstRowPitch, 0,
conversionRow.data());
}
else
......@@ -1452,6 +1530,7 @@ angle::Result TextureMtl::convertAndSetPerSliceSubImage(const gl::Context *conte
}
}
} // if (mFormat.intendedAngleFormat().isBlock)
} // if (unpackBuffer)
return angle::Result::Continue;
}
......
......@@ -513,10 +513,11 @@ angle::Result VertexArrayMtl::convertIndexBuffer(const gl::Context *glContext,
ASSERT((offset % mtl::kIndexBufferOffsetAlignment) != 0 ||
indexType == gl::DrawElementsType::UnsignedByte);
ContextMtl *contextMtl = mtl::GetImpl(glContext);
BufferMtl *idxBuffer = mtl::GetImpl(getState().getElementArrayBuffer());
IndexConversionBufferMtl *conversion =
idxBuffer->getIndexConversionBuffer(glContext, indexType, offset);
idxBuffer->getIndexConversionBuffer(contextMtl, indexType, offset);
// Has the content of the buffer has changed since last conversion?
if (!conversion->dirty)
......@@ -594,16 +595,16 @@ angle::Result VertexArrayMtl::convertVertexBuffer(const gl::Context *glContext,
size_t attribIndex,
const mtl::VertexFormat &srcVertexFormat)
{
ContextMtl *contextMtl = mtl::GetImpl(glContext);
const angle::Format &intendedAngleFormat = srcVertexFormat.intendedAngleFormat();
ConversionBufferMtl *conversion = srcBuffer->getVertexConversionBuffer(
glContext, intendedAngleFormat.id, binding.getStride(), binding.getOffset());
contextMtl, intendedAngleFormat.id, binding.getStride(), binding.getOffset());
// Has the content of the buffer has changed since last conversion?
if (!conversion->dirty)
{
ContextMtl *contextMtl = mtl::GetImpl(glContext);
// Buffer's data hasn't been changed. Re-use last converted results
GLuint stride;
const mtl::VertexFormat &vertexFormat =
......@@ -646,7 +647,7 @@ angle::Result VertexArrayMtl::convertVertexBufferCPU(const gl::Context *glContex
return angle::Result::Continue;
}
const uint8_t *srcBytes = srcBuffer->getClientShadowCopyData(glContext);
const uint8_t *srcBytes = srcBuffer->getClientShadowCopyData(contextMtl);
ANGLE_CHECK_GL_ALLOC(contextMtl, srcBytes);
srcBytes += binding.getOffset();
......
......@@ -505,6 +505,17 @@ class BlitCommandEncoder final : public CommandEncoder
MTLOrigin dstOrigin,
MTLBlitOption blitOption);
BlitCommandEncoder &copyTextureToBuffer(const TextureRef &src,
uint32_t srcSlice,
uint32_t srcLevel,
MTLOrigin srcOrigin,
MTLSize srcSize,
const BufferRef &dst,
size_t dstOffset,
size_t dstBytesPerRow,
size_t dstBytesPerImage,
MTLBlitOption blitOption);
BlitCommandEncoder &copyTexture(const TextureRef &src,
uint32_t srcSlice,
uint32_t srcLevel,
......
......@@ -1605,6 +1605,40 @@ BlitCommandEncoder &BlitCommandEncoder::copyBufferToTexture(const BufferRef &src
return *this;
}
BlitCommandEncoder &BlitCommandEncoder::copyTextureToBuffer(const TextureRef &src,
uint32_t srcSlice,
uint32_t srcLevel,
MTLOrigin srcOrigin,
MTLSize srcSize,
const BufferRef &dst,
size_t dstOffset,
size_t dstBytesPerRow,
size_t dstBytesPerImage,
MTLBlitOption blitOption)
{
if (!src || !dst)
{
return *this;
}
cmdBuffer().setReadDependency(src);
cmdBuffer().setWriteDependency(dst);
[get() copyFromTexture:src->get()
sourceSlice:srcSlice
sourceLevel:srcLevel
sourceOrigin:srcOrigin
sourceSize:srcSize
toBuffer:dst->get()
destinationOffset:dstOffset
destinationBytesPerRow:dstBytesPerRow
destinationBytesPerImage:dstBytesPerImage
options:blitOption];
return *this;
}
BlitCommandEncoder &BlitCommandEncoder::copyTexture(const TextureRef &src,
uint32_t srcStartSlice,
uint32_t srcStartLevel,
......
......@@ -115,6 +115,7 @@ constexpr uint32_t kUniformBufferSettingOffsetMinAlignment = 256;
constexpr uint32_t kUniformBufferSettingOffsetMinAlignment = 4;
#endif
constexpr uint32_t kIndexBufferOffsetAlignment = 4;
constexpr uint32_t kTextureToBufferBlittingAlignment = 256;
// Front end binding limits
constexpr uint32_t kMaxGLSamplerBindings = 2 * kMaxShaderSamplers;
......@@ -138,6 +139,21 @@ constexpr size_t kOcclusionQueryResultSize = sizeof(uint64_t);
// NOTE(hqle): Support ES 3.0.
constexpr gl::Version kMaxSupportedGLVersion = gl::Version(2, 0);
// Work-around the enum is not available on macOS
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
constexpr MTLBlitOption kBlitOptionRowLinearPVRTC = MTLBlitOptionNone;
#else
constexpr MTLBlitOption kBlitOptionRowLinearPVRTC = MTLBlitOptionRowLinearPVRTC;
#endif
enum class PixelType
{
Int,
UInt,
Float,
EnumCount,
};
template <typename T>
struct ImplTypeHelper;
......
......@@ -128,6 +128,33 @@ struct IndexGenerationParams
uint32_t dstOffset;
};
struct CopyPixelsCommonParams
{
BufferRef buffer;
uint32_t bufferStartOffset = 0;
uint32_t bufferRowPitch = 0;
TextureRef texture;
};
struct CopyPixelsFromBufferParams : CopyPixelsCommonParams
{
uint32_t bufferDepthPitch = 0;
// z offset is:
// - slice index if texture is array.
// - depth if texture is 3d.
gl::Box textureArea;
};
struct CopyPixelsToBufferParams : CopyPixelsCommonParams
{
gl::Rectangle textureArea;
uint32_t textureLevel = 0;
uint32_t textureSliceOrDeph = 0;
bool reverseTextureRowOrder;
};
// Utils class for clear & blitting
class ClearUtils final : angle::NonCopyable
{
......@@ -353,6 +380,40 @@ class MipmapUtils final : angle::NonCopyable
AutoObjCPtr<id<MTLComputePipelineState>> m3DMipGeneratorPipeline;
};
// Util class for handling pixels copy between buffers and textures
class CopyPixelsUtils
{
public:
CopyPixelsUtils() = default;
CopyPixelsUtils(const std::string &readShaderName, const std::string &writeShaderName);
CopyPixelsUtils(const CopyPixelsUtils &src);
void onDestroy();
angle::Result unpackPixelsFromBufferToTexture(ContextMtl *contextMtl,
const angle::Format &srcAngleFormat,
const CopyPixelsFromBufferParams &params);
angle::Result packPixelsFromTextureToBuffer(ContextMtl *contextMtl,
const angle::Format &dstAngleFormat,
const CopyPixelsToBufferParams &params);
private:
AutoObjCPtr<id<MTLComputePipelineState>> getPixelsCopyPipeline(ContextMtl *contextMtl,
const angle::Format &angleFormat,
const TextureRef &texture,
bool bufferWrite);
// Copy pixels between buffer and texture compute pipelines:
// - First dimension: pixel format.
// - Second dimension: texture type * (buffer read/write flag)
using PixelsCopyPipelineArray = std::array<
std::array<AutoObjCPtr<id<MTLComputePipelineState>>, mtl_shader::kTextureTypeCount * 2>,
angle::kNumANGLEFormats>;
PixelsCopyPipelineArray mPixelsCopyPipelineCaches;
const std::string mReadShaderName;
const std::string mWriteShaderName;
};
// RenderUtils: container class of various util classes above
class RenderUtils : public Context, angle::NonCopyable
{
......@@ -411,6 +472,13 @@ class RenderUtils : public Context, angle::NonCopyable
bool sRGBMipmap,
gl::TexLevelArray<mtl::TextureRef> *mipmapOutputViews);
angle::Result unpackPixelsFromBufferToTexture(ContextMtl *contextMtl,
const angle::Format &srcAngleFormat,
const CopyPixelsFromBufferParams &params);
angle::Result packPixelsFromTextureToBuffer(ContextMtl *contextMtl,
const angle::Format &dstAngleFormat,
const CopyPixelsToBufferParams &params);
private:
// override ErrorHandler
void handleError(GLenum error,
......@@ -428,6 +496,7 @@ class RenderUtils : public Context, angle::NonCopyable
IndexGeneratorUtils mIndexUtils;
VisibilityResultUtils mVisibilityResultUtils;
MipmapUtils mMipmapUtils;
std::array<CopyPixelsUtils, angle::EnumSize<PixelType>()> mCopyPixelsUtils;
};
} // namespace mtl
......
......@@ -56,6 +56,15 @@ angle::Result ReadTexturePerSliceBytes(const gl::Context *context,
uint32_t sliceOrDepth,
uint8_t *dataOut);
angle::Result ReadTexturePerSliceBytesToBuffer(const gl::Context *context,
const TextureRef &texture,
size_t bytesPerRow,
const gl::Rectangle &fromRegion,
uint32_t mipLevel,
uint32_t sliceOrDepth,
uint32_t dstOffset,
const BufferRef &dstBuffer);
MTLViewport GetViewport(const gl::Rectangle &rect, double znear = 0, double zfar = 1);
MTLViewport GetViewportFlipY(const gl::Rectangle &rect,
NSUInteger screenHeight,
......@@ -132,6 +141,8 @@ bool IsFormatEmulated(const mtl::Format &mtlFormat);
// has alpha channel.
MTLClearColor EmulatedAlphaClearColor(MTLClearColor color, MTLColorWriteMask colorMask);
gl::Box MTLRegionToGLBox(const MTLRegion &mtlRegion);
NS_ASSUME_NONNULL_END
} // namespace mtl
} // namespace rx
......
......@@ -321,6 +321,42 @@ angle::Result ReadTexturePerSliceBytes(const gl::Context *context,
return angle::Result::Continue;
}
angle::Result ReadTexturePerSliceBytesToBuffer(const gl::Context *context,
const TextureRef &texture,
size_t bytesPerRow,
const gl::Rectangle &fromRegion,
uint32_t mipLevel,
uint32_t sliceOrDepth,
uint32_t dstOffset,
const BufferRef &dstBuffer)
{
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);
BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder();
blitEncoder->copyTextureToBuffer(texture, layer, mipLevel, mtlRegion.origin, mtlRegion.size,
dstBuffer, dstOffset, bytesPerRow, 0, MTLBlitOptionNone);
return angle::Result::Continue;
}
MTLViewport GetViewport(const gl::Rectangle &rect, double znear, double zfar)
{
MTLViewport re;
......@@ -783,5 +819,12 @@ MTLClearColor EmulatedAlphaClearColor(MTLClearColor color, MTLColorWriteMask col
return re;
}
gl::Box MTLRegionToGLBox(const MTLRegion &mtlRegion)
{
return gl::Box(static_cast<int>(mtlRegion.origin.x), static_cast<int>(mtlRegion.origin.y),
static_cast<int>(mtlRegion.origin.z), static_cast<int>(mtlRegion.size.width),
static_cast<int>(mtlRegion.size.height), static_cast<int>(mtlRegion.size.depth));
}
} // namespace mtl
} // namespace rx
......@@ -11,6 +11,7 @@
#include <array>
#include "test_utils/gl_raii.h"
#include "util/random_utils.h"
using namespace angle;
......@@ -61,21 +62,23 @@ TEST_P(ReadPixelsTest, OutOfBounds)
}
}
class ReadPixelsPBOTest : public ReadPixelsTest
class ReadPixelsPBONVTest : public ReadPixelsTest
{
protected:
ReadPixelsPBOTest() : mPBO(0), mTexture(0), mFBO(0) {}
ReadPixelsPBONVTest() : mPBO(0), mTexture(0), mFBO(0) {}
void testSetUp() override
{
glGenBuffers(1, &mPBO);
glGenFramebuffers(1, &mFBO);
Reset(4 * getWindowWidth() * getWindowHeight(), 4, 1);
Reset(4 * getWindowWidth() * getWindowHeight(), 4, 4);
}
void Reset(GLuint bufferSize, GLuint fboWidth, GLuint fboHeight)
virtual void Reset(GLuint bufferSize, GLuint fboWidth, GLuint fboHeight)
{
ANGLE_SKIP_TEST_IF(!hasPBOExts());
glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
glBufferData(GL_PIXEL_PACK_BUFFER, bufferSize, nullptr, GL_STATIC_DRAW);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
......@@ -83,7 +86,9 @@ class ReadPixelsPBOTest : public ReadPixelsTest
glDeleteTextures(1, &mTexture);
glGenTextures(1, &mTexture);
glBindTexture(GL_TEXTURE_2D, mTexture);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, fboWidth, fboHeight);
glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, fboWidth, fboHeight);
mFBOWidth = fboWidth;
mFBOHeight = fboHeight;
glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
......@@ -99,9 +104,183 @@ class ReadPixelsPBOTest : public ReadPixelsTest
glDeleteFramebuffers(1, &mFBO);
}
bool hasPBOExts() const
{
return IsGLExtensionEnabled("GL_NV_pixel_buffer_object") &&
IsGLExtensionEnabled("GL_EXT_texture_storage");
}
GLuint mPBO = 0;
GLuint mTexture = 0;
GLuint mFBO = 0;
GLuint mFBOWidth = 0;
GLuint mFBOHeight = 0;
};
// Test basic usage of PBOs.
TEST_P(ReadPixelsPBONVTest, Basic)
{
ANGLE_SKIP_TEST_IF(!hasPBOExts() || !IsGLExtensionEnabled("GL_EXT_map_buffer_range") ||
!IsGLExtensionEnabled("GL_OES_mapbuffer"));
// http://anglebug.com/5022
ANGLE_SKIP_TEST_IF(IsWindows() && IsDesktopOpenGL());
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Clear last pixel to green
glScissor(15, 15, 1, 1);
glEnable(GL_SCISSOR_TEST);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_GL_NO_ERROR();
glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0);
void *mappedPtr = glMapBufferRangeEXT(GL_PIXEL_PACK_BUFFER, 0, 32, GL_MAP_READ_BIT);
GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
EXPECT_GL_NO_ERROR();
EXPECT_EQ(GLColor::red, dataColor[0]);
EXPECT_EQ(GLColor::red, dataColor[16 * 16 - 2]);
EXPECT_EQ(GLColor::green, dataColor[16 * 16 - 1]);
glUnmapBufferOES(GL_PIXEL_PACK_BUFFER);
EXPECT_GL_NO_ERROR();
}
// Test that calling SubData preserves PBO data.
TEST_P(ReadPixelsPBONVTest, SubDataPreservesContents)
{
ANGLE_SKIP_TEST_IF(!hasPBOExts() || !IsGLExtensionEnabled("GL_EXT_map_buffer_range") ||
!IsGLExtensionEnabled("GL_OES_mapbuffer"));
// anglebug.com/2185
ANGLE_SKIP_TEST_IF(IsOSX() && IsNVIDIA() && IsDesktopOpenGL());
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_GL_NO_ERROR();
glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0);
unsigned char data[4] = {1, 2, 3, 4};
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, mPBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, 4, data);
void *mappedPtr = glMapBufferRangeEXT(GL_ARRAY_BUFFER, 0, 32, GL_MAP_READ_BIT);
GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
EXPECT_GL_NO_ERROR();
EXPECT_EQ(GLColor(1, 2, 3, 4), dataColor[0]);
EXPECT_EQ(GLColor::red, dataColor[1]);
glUnmapBufferOES(GL_ARRAY_BUFFER);
EXPECT_GL_NO_ERROR();
}
// Test that calling ReadPixels with GL_DYNAMIC_DRAW buffer works
TEST_P(ReadPixelsPBONVTest, DynamicPBO)
{
ANGLE_SKIP_TEST_IF(!hasPBOExts() || !IsGLExtensionEnabled("GL_EXT_map_buffer_range") ||
!IsGLExtensionEnabled("GL_OES_mapbuffer"));
// anglebug.com/2185
ANGLE_SKIP_TEST_IF(IsOSX() && IsNVIDIA() && IsDesktopOpenGL());
glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
glBufferData(GL_PIXEL_PACK_BUFFER, 4 * getWindowWidth() * getWindowHeight(), nullptr,
GL_DYNAMIC_DRAW);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_GL_NO_ERROR();
glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0);
unsigned char data[4] = {1, 2, 3, 4};
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, mPBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, 4, data);
void *mappedPtr = glMapBufferRangeEXT(GL_ARRAY_BUFFER, 0, 32, GL_MAP_READ_BIT);
GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
EXPECT_GL_NO_ERROR();
EXPECT_EQ(GLColor(1, 2, 3, 4), dataColor[0]);
EXPECT_EQ(GLColor::red, dataColor[1]);
glUnmapBufferOES(GL_ARRAY_BUFFER);
EXPECT_GL_NO_ERROR();
}
TEST_P(ReadPixelsPBONVTest, ReadFromFBO)
{
ANGLE_SKIP_TEST_IF(!hasPBOExts() || !IsGLExtensionEnabled("GL_EXT_map_buffer_range") ||
!IsGLExtensionEnabled("GL_OES_mapbuffer"));
glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
glViewport(0, 0, mFBOWidth, mFBOHeight);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Clear last pixel to green
glScissor(mFBOWidth - 1, mFBOHeight - 1, 1, 1);
glEnable(GL_SCISSOR_TEST);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_GL_NO_ERROR();
glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
glReadPixels(0, 0, mFBOWidth, mFBOHeight, GL_RGBA, GL_UNSIGNED_BYTE, 0);
void *mappedPtr =
glMapBufferRangeEXT(GL_PIXEL_PACK_BUFFER, 0, 4 * mFBOWidth * mFBOHeight, GL_MAP_READ_BIT);
GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
EXPECT_GL_NO_ERROR();
EXPECT_EQ(GLColor::red, dataColor[0]);
EXPECT_EQ(GLColor::red, dataColor[mFBOWidth * mFBOHeight - 2]);
EXPECT_EQ(GLColor::green, dataColor[mFBOWidth * mFBOHeight - 1]);
glUnmapBufferOES(GL_PIXEL_PACK_BUFFER);
EXPECT_GL_NO_ERROR();
}
class ReadPixelsPBOTest : public ReadPixelsPBONVTest
{
protected:
ReadPixelsPBOTest() : ReadPixelsPBONVTest() {}
void testSetUp() override
{
glGenBuffers(1, &mPBO);
glGenFramebuffers(1, &mFBO);
Reset(4 * getWindowWidth() * getWindowHeight(), 4, 1);
}
void Reset(GLuint bufferSize, GLuint fboWidth, GLuint fboHeight) override
{
glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
glBufferData(GL_PIXEL_PACK_BUFFER, bufferSize, nullptr, GL_STATIC_DRAW);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glDeleteTextures(1, &mTexture);
glGenTextures(1, &mTexture);
glBindTexture(GL_TEXTURE_2D, mTexture);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, fboWidth, fboHeight);
glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
ASSERT_GL_NO_ERROR();
}
};
// Test basic usage of PBOs.
......@@ -696,6 +875,7 @@ TEST_P(ReadPixelsErrorTest, ReadBufferIsNone)
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST_ES2(ReadPixelsTest);
ANGLE_INSTANTIATE_TEST_ES2(ReadPixelsPBONVTest);
ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsPBOTest);
ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsPBODrawTest);
ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsMultisampleTest);
......
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