Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow SkImage::asyncRescaleAndReadPixels() to work async on WebGL #169

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/gpu/gl/GrGLFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ using GrGLGenSamplersFn = GrGLvoid GR_GL_FUNCTION_TYPE(GrGLsizei count, GrGLuint
using GrGLGenTexturesFn = GrGLvoid GR_GL_FUNCTION_TYPE(GrGLsizei n, GrGLuint* textures);
using GrGLGenVertexArraysFn = GrGLvoid GR_GL_FUNCTION_TYPE(GrGLsizei n, GrGLuint* arrays);
using GrGLGetBufferParameterivFn = GrGLvoid GR_GL_FUNCTION_TYPE(GrGLenum target, GrGLenum pname, GrGLint* params);
using GrGLGetBufferSubDataFn = GrGLvoid GR_GL_FUNCTION_TYPE(GrGLenum target, GrGLintptr offset, GrGLsizeiptr size, GrGLvoid* data);
using GrGLGetErrorFn = GrGLenum GR_GL_FUNCTION_TYPE();
using GrGLGetFramebufferAttachmentParameterivFn = GrGLvoid GR_GL_FUNCTION_TYPE(GrGLenum target, GrGLenum attachment, GrGLenum pname, GrGLint* params);
using GrGLGetFloatvFn = GrGLvoid GR_GL_FUNCTION_TYPE(GrGLenum pname, GrGLfloat* params);
Expand Down
1 change: 1 addition & 0 deletions include/gpu/gl/GrGLInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ struct SK_API GrGLInterface : public SkRefCnt {
GrGLFunction<GrGLGenTexturesFn> fGenTextures;
GrGLFunction<GrGLGenVertexArraysFn> fGenVertexArrays;
GrGLFunction<GrGLGetBufferParameterivFn> fGetBufferParameteriv;
GrGLFunction<GrGLGetBufferSubDataFn> fGetBufferSubData;
GrGLFunction<GrGLGetErrorFn> fGetError;
GrGLFunction<GrGLGetFramebufferAttachmentParameterivFn> fGetFramebufferAttachmentParameteriv;
GrGLFunction<GrGLGetFloatvFn> fGetFloatv;
Expand Down
39 changes: 28 additions & 11 deletions src/gpu/AsyncReadTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,19 +152,36 @@ class TAsyncReadResult : public SkImage::AsyncReadResult {
SkISize dimensions,
size_t rowBytes,
TClientMappedBufferManager<T, IDType>* manager) {
size_t size = rowBytes*dimensions.height();
auto doConvert = [converter{result.fPixelConverter}, size](const void* src) -> sk_sp<SkData> {
if (converter) {
sk_sp<SkData> data = SkData::MakeUninitialized(size);
converter(data->writable_data(), src);
return data;
}
return nullptr;
};

const void* mappedData = result.fTransferBuffer->map();
if (!mappedData) {
return false;
}
if (result.fPixelConverter) {
size_t size = rowBytes*dimensions.height();
sk_sp<SkData> data = SkData::MakeUninitialized(size);
result.fPixelConverter(data->writable_data(), mappedData);
this->addCpuPlane(std::move(data), rowBytes);
result.fTransferBuffer->unmap();

if (mappedData) {
if (auto converted = doConvert(mappedData)) {
this->addCpuPlane(std::move(converted), rowBytes);
result.fTransferBuffer->unmap();
} else {
manager->insert(result.fTransferBuffer);
this->addMappedPlane(mappedData, rowBytes, std::move(result.fTransferBuffer));
}
} else {
manager->insert(result.fTransferBuffer);
this->addMappedPlane(mappedData, rowBytes, std::move(result.fTransferBuffer));
sk_sp<SkData> tmp = SkData::MakeUninitialized(size);
if (!result.fTransferBuffer->getData(tmp->writable_data(), 0, size)) {
return false;
}
if (auto converted = doConvert(tmp->data())) {
this->addCpuPlane(std::move(converted), rowBytes);
} else {
this->addCpuPlane(std::move(tmp), rowBytes);
}
}
return true;
}
Expand Down
19 changes: 19 additions & 0 deletions src/gpu/ganesh/GrGpuBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,25 @@ bool GrGpuBuffer::updateData(const void* src, size_t offset, size_t size, bool p
return this->onUpdateData(src, offset, size, preserve);
}

bool GrGpuBuffer::getData(void* dst, size_t offset, size_t size) {
SkASSERT(!this->isMapped());
SkASSERT(size > 0 && offset + size <= fSizeInBytes);
SkASSERT(dst);

if (this->wasDestroyed()) {
return false;
}

const void* mapped = this->map();
if (mapped != nullptr) {
memcpy(dst, (const char*)mapped + offset, size);
this->unmap();
return true;
}

return this->onGetData(dst, offset, size);
}

void GrGpuBuffer::ComputeScratchKeyForDynamicBuffer(size_t size,
GrGpuBufferType intendedType,
skgpu::ScratchKey* key) {
Expand Down
17 changes: 17 additions & 0 deletions src/gpu/ganesh/GrGpuBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,22 @@ class GrGpuBuffer : public GrGpuResource, public GrBuffer {
*/
bool updateData(const void* src, size_t offset, size_t size, bool preserve);

/**
* Get the buffer data.
*
* WebGL's buffer can't be mapped to client side. Use this function to get the buffer data.
*
* The data is always copied to client side. Will try copy from mapped if supported.
*
* The buffer must not be mapped.
*
* Note that buffer updates do not go through GrContext and therefore are not serialized with
* other operations.
*
* @return returns true if succeeds, false otherwise.
*/
bool getData(void* dst, size_t offset, size_t size);

GrGpuBufferType intendedType() const { return fIntendedType; }

protected:
Expand Down Expand Up @@ -129,6 +145,7 @@ class GrGpuBuffer : public GrGpuResource, public GrBuffer {
virtual void onUnmap(MapType) = 0;
virtual bool onClearToZero() = 0;
virtual bool onUpdateData(const void* src, size_t offset, size_t size, bool preserve) = 0;
virtual bool onGetData(void* dst, size_t offset, size_t size) { return false; }

size_t onGpuMemorySize() const override { return fSizeInBytes; }
void onSetLabel() override{}
Expand Down
2 changes: 2 additions & 0 deletions src/gpu/ganesh/gl/GrGLAssembleGLInterfaceAutogen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ sk_sp<const GrGLInterface> GrGLMakeAssembledGLInterface(void *ctx, GrGLGetProc g
GET_PROC(DrawBuffer);
GET_PROC(PolygonMode);

GET_PROC(GetBufferSubData);

if (glVer >= GR_GL_VER(3,0)) {
GET_PROC(GetStringi);
}
Expand Down
4 changes: 4 additions & 0 deletions src/gpu/ganesh/gl/GrGLAssembleWebGLInterfaceAutogen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ sk_sp<const GrGLInterface> GrGLMakeAssembledWebGLInterface(void *ctx, GrGLGetPro
GET_PROC(VertexAttribPointer);
GET_PROC(Viewport);

if (glVer >= GR_GL_VER(2,0)) {
GET_PROC(GetBufferSubData);
}

if (glVer >= GR_GL_VER(2,0)) {
GET_PROC(GetStringi);
}
Expand Down
13 changes: 13 additions & 0 deletions src/gpu/ganesh/gl/GrGLBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,19 @@ bool GrGLBuffer::onUpdateData(const void* src, size_t offset, size_t size, bool
return true;
}

bool GrGLBuffer::onGetData(void* dst, size_t offset, size_t size) {
SkASSERT(fBufferID);

if (!this->glCaps().getBufferSubDataSupport()) {
return false;
}

// bindbuffer handles dirty context
GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
GL_CALL(GetBufferSubData(target, offset, size, dst));
return true;
}

void GrGLBuffer::onSetLabel() {
SkASSERT(fBufferID);
if (!this->getLabel().empty()) {
Expand Down
1 change: 1 addition & 0 deletions src/gpu/ganesh/gl/GrGLBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class GrGLBuffer : public GrGpuBuffer {
void onUnmap(MapType) override;
bool onClearToZero() override;
bool onUpdateData(const void* src, size_t offset, size_t size, bool preserve) override;
bool onGetData(void* dst, size_t offset, size_t size) override;

void onSetLabel() override;

Expand Down
25 changes: 20 additions & 5 deletions src/gpu/ganesh/gl/GrGLCaps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ GrGLCaps::GrGLCaps(const GrContextOptions& contextOptions,
fFBFetchRequiresEnablePerSample = false;
fSRGBWriteControl = false;
fSkipErrorChecks = false;
fGetBufferSubDataSupport = false;

fShaderCaps = std::make_unique<GrShaderCaps>();

Expand Down Expand Up @@ -400,6 +401,12 @@ void GrGLCaps::init(const GrContextOptions& contextOptions,
return SkToBool(contextFlags & GR_GL_CONTEXT_FLAG_PROTECTED_CONTENT_BIT_EXT);
}();

if (GR_IS_GR_GL(standard)) {
fGetBufferSubDataSupport = true;
} else if (GR_IS_GR_WEBGL(standard)) {
fGetBufferSubDataSupport = version >= GR_GL_VER(2, 0);
}

/**************************************************************************
* GrShaderCaps fields
**************************************************************************/
Expand Down Expand Up @@ -559,11 +566,6 @@ void GrGLCaps::init(const GrContextOptions& contextOptions,
fMapBufferFlags = kNone_MapFlags;
}

// Buffers have more restrictions in WebGL than GLES. For example,
// https://www.khronos.org/registry/webgl/specs/latest/2.0/#BUFFER_OBJECT_BINDING
// We therefore haven't attempted to support mapping or transfers between buffers and surfaces
// or between buffers.

if (GR_IS_GR_GL(standard)) {
if (version >= GR_GL_VER(2, 1) || ctxInfo.hasExtension("GL_ARB_pixel_buffer_object") ||
ctxInfo.hasExtension("GL_EXT_pixel_buffer_object")) {
Expand All @@ -589,6 +591,12 @@ void GrGLCaps::init(const GrContextOptions& contextOptions,
// fTransferFromSurfaceToBufferSupport = false;
// fTransferBufferType = TransferBufferType::kChromium;
}
} else if (GR_IS_GR_WEBGL(standard)) {
if (version >= GR_GL_VER(2, 0)) {
fTransferFromBufferToTextureSupport = true;
fTransferFromSurfaceToBufferSupport = true;
fTransferBufferType = TransferBufferType::kARB_PBO;
}
}

if (GR_IS_GR_GL(standard) &&
Expand All @@ -597,6 +605,13 @@ void GrGLCaps::init(const GrContextOptions& contextOptions,
} else if (GR_IS_GR_GL_ES(standard) &&
(version >= GR_GL_VER(3, 0) || ctxInfo.hasExtension("GL_NV_copy_buffer"))) {
fTransferFromBufferToBufferSupport = true;
} else if (GR_IS_GR_WEBGL(standard)) {
if (version >= GR_GL_VER(2, 0)) {
// WebGL has more restriction about buffer binding
// https://registry.khronos.org/webgl/specs/latest/2.0/#COPYING_BUFFERS
// TODO: make sure index buffer is handled properly to enable this
// fTransferFromBufferToBufferSupport = true;
}
}

// On many GPUs, map memory is very expensive, so we effectively disable it here by setting the
Expand Down
3 changes: 3 additions & 0 deletions src/gpu/ganesh/gl/GrGLCaps.h
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,8 @@ class GrGLCaps : public GrCaps {

bool clientCanDisableMultisample() const { return fClientCanDisableMultisample; }

bool getBufferSubDataSupport() const { return fGetBufferSubDataSupport; }

GrBackendFormat getBackendFormatFromCompressionType(SkTextureCompressionType) const override;

skgpu::Swizzle getWriteSwizzle(const GrBackendFormat&, GrColorType) const override;
Expand Down Expand Up @@ -630,6 +632,7 @@ class GrGLCaps : public GrCaps {
bool fSRGBWriteControl : 1;
bool fSkipErrorChecks : 1;
bool fClientCanDisableMultisample : 1;
bool fGetBufferSubDataSupport: 1;

// Driver workarounds
bool fDoManualMipmapping : 1;
Expand Down
8 changes: 8 additions & 0 deletions src/gpu/ganesh/gl/GrGLInterfaceAutogen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,14 @@ bool GrGLInterface::validate() const {
}
}

if (GR_IS_GR_GL(fStandard) ||
(GR_IS_GR_WEBGL(fStandard) && (
(glVer >= GR_GL_VER(2,0))))) {
if (!fFunctions.fGetBufferSubData) {
RETURN_FALSE_INTERFACE;
}
}

if ((GR_IS_GR_GL(fStandard) && (
(glVer >= GR_GL_VER(3,0)))) ||
(GR_IS_GR_GL_ES(fStandard) && (
Expand Down
56 changes: 28 additions & 28 deletions tests/TransferPixelsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,25 @@ void basic_transfer_to_test(skiatest::Reporter* reporter,
if (!buffer) {
return;
}
void* data = buffer->map();
if (!buffer) {
ERRORF(reporter, "Could not map buffer");
return;
}
memcpy(data, srcData.get(), size);
buffer->unmap();
auto updateData = [buffer, caps, gpu, reporter] (const void* src, size_t size, bool preserve) {
if (GrCaps::kNone_MapFlags != caps->mapBufferFlags()) {
void *map = buffer->map();
REPORTER_ASSERT(reporter, map);
if (!map) {
ERRORF(reporter, "Failed to map transfer buffer.");
return;
}
memcpy(map, src, size);
buffer->unmap();
} else {
if (!buffer->updateData(src, 0, size, preserve)) {
ERRORF(reporter, "Could not updateData");
}
gpu->submitToGpu(GrSyncCpu::kYes);
}
};

updateData(srcData.get(), size, false);

//////////////////////////
// transfer full data
Expand Down Expand Up @@ -278,9 +290,7 @@ void basic_transfer_to_test(skiatest::Reporter* reporter,
// change color of subrectangle
fill_transfer_data(left, top, width, height, srcRowBytes, allowedSrc.fColorType,
srcData.get());
data = buffer->map();
memcpy(data, srcData.get(), size);
buffer->unmap();
updateData(srcData.get(), size, true);

result = gpu->transferPixelsTo(tex.get(),
SkIRect::MakeXYWH(left, top, width, height),
Expand Down Expand Up @@ -393,7 +403,7 @@ void basic_transfer_from_test(skiatest::Reporter* reporter, const sk_gpu_test::C

sk_sp<GrGpuBuffer> buffer = resourceProvider->createBuffer(bufferSize,
GrGpuBufferType::kXferGpuToCpu,
kDynamic_GrAccessPattern,
kStream_GrAccessPattern,
GrResourceProvider::ZeroInit::kNo);
REPORTER_ASSERT(reporter, buffer);
if (!buffer) {
Expand Down Expand Up @@ -422,15 +432,12 @@ void basic_transfer_from_test(skiatest::Reporter* reporter, const sk_gpu_test::C
}

// Copy the transfer buffer contents to a temporary so we can manipulate it.
const auto* map = reinterpret_cast<const char*>(buffer->map());
REPORTER_ASSERT(reporter, map);
if (!map) {
ERRORF(reporter, "Failed to map transfer buffer.");
return;
size_t copy_size = kTexDims.fHeight * fullBufferRowBytes;
std::unique_ptr<char[]> transferData(new char[copy_size]);

if (!buffer->getData(transferData.get(), 0, copy_size)) {
ERRORF(reporter, "Could not getData");
}
std::unique_ptr<char[]> transferData(new char[kTexDims.fHeight * fullBufferRowBytes]);
memcpy(transferData.get(), map, fullBufferRowBytes * kTexDims.fHeight);
buffer->unmap();

GrImageInfo transferInfo(allowedRead.fColorType, kUnpremul_SkAlphaType, nullptr, kTexDims);

Expand Down Expand Up @@ -468,15 +475,8 @@ void basic_transfer_from_test(skiatest::Reporter* reporter, const sk_gpu_test::C
gpu->submitToGpu(GrSyncCpu::kYes);
}

map = reinterpret_cast<const char*>(buffer->map());
REPORTER_ASSERT(reporter, map);
if (!map) {
ERRORF(reporter, "Failed to map transfer buffer.");
return;
}
const char* bufferStart = reinterpret_cast<const char*>(map) + partialReadOffset;
memcpy(transferData.get(), bufferStart, partialBufferRowBytes * kTexDims.fHeight);
buffer->unmap();
if (!buffer->getData(transferData.get(), partialReadOffset, partialBufferRowBytes *
kTexDims.fHeight)) { ERRORF(reporter, "Could not getData"); }

transferInfo = transferInfo.makeWH(kPartialWidth, kPartialHeight);
const char* textureDataStart =
Expand Down
9 changes: 9 additions & 0 deletions tools/gpu/gl/interface/interface.json5
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@
"DrawBuffer", "PolygonMode",
],
},
{
"GL": [{"ext": "<core>"}],
"GLES": null,
"WebGL": [{"min_version": [2, 0], "ext": "<core>"}],

"functions": [
"GetBufferSubData",
],
},
{
"GL": [{"min_version": [3, 0], "ext": "<core>"}],
"GLES": [{"min_version": [3, 0], "ext": "<core>"}],
Expand Down