Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(564)

Unified Diff: third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp

Issue 2379203002: implement getBufferSubDataAsync prototype (Closed)
Patch Set: Merge branch 'master' into async Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp
diff --git a/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp
index 3d352b67a928bbbc0d3d3414aab1d80bae729430..66f0917739fc3cbaba7543f8ede320ca1f2d7b7c 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp
@@ -5,11 +5,13 @@
#include "modules/webgl/WebGL2RenderingContextBase.h"
#include "bindings/modules/v8/WebGLAny.h"
+#include "core/dom/DOMException.h"
#include "core/frame/ImageBitmap.h"
#include "core/html/HTMLCanvasElement.h"
#include "core/html/HTMLImageElement.h"
#include "core/html/HTMLVideoElement.h"
#include "core/html/ImageData.h"
+#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "modules/webgl/WebGLActiveInfo.h"
#include "modules/webgl/WebGLBuffer.h"
@@ -40,6 +42,8 @@ GLsync syncObjectOrZero(const WebGLSync* object) {
return object ? object->object() : nullptr;
}
+// TODO(kainino): Change outByteLength to GLuint and change the associated
+// range checking (and all uses) - overflow becomes possible in cases below
bool validateSubSourceAndGetData(DOMArrayBufferView* view,
GLuint subOffset,
GLuint subLength,
@@ -148,6 +152,79 @@ const GLenum kCompressedTextureFormatsETC2EAC[] = {
GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC,
};
+class WebGLGetBufferSubDataAsyncCallback
+ : public RefCounted<WebGLGetBufferSubDataAsyncCallback> {
+ public:
+ WebGLGetBufferSubDataAsyncCallback(
+ WebGL2RenderingContextBase* context,
+ ScriptPromiseResolver* promiseResolver,
+ void* shmReadbackResultData,
+ GLuint commandsIssuedQueryID,
+ DOMArrayBufferView* destinationArrayBufferView,
+ void* destinationDataPtr,
+ long long destinationByteLength)
+ : context(context),
+ promiseResolver(promiseResolver),
+ shmReadbackResultData(shmReadbackResultData),
+ commandsIssuedQueryID(commandsIssuedQueryID),
+ destinationArrayBufferView(destinationArrayBufferView),
+ destinationDataPtr(destinationDataPtr),
+ destinationByteLength(destinationByteLength) {
+ DCHECK(shmReadbackResultData);
+ DCHECK(destinationDataPtr);
+ }
+
+ void destroy() {
+ DCHECK(shmReadbackResultData);
+ context->contextGL()->FreeSharedMemory(shmReadbackResultData);
+ shmReadbackResultData = nullptr;
+ DOMException* exception =
+ DOMException::create(InvalidStateError, "Context lost or destroyed");
+ this->promiseResolver->reject(exception);
+ }
+
+ void resolve() {
+ if (!this->context || !this->shmReadbackResultData) {
+ DOMException* exception =
+ DOMException::create(InvalidStateError, "Context lost or destroyed");
+ this->promiseResolver->reject(exception);
+ return;
+ }
+ if (this->destinationArrayBufferView->buffer()->isNeutered()) {
+ DOMException* exception = DOMException::create(
+ InvalidStateError, "ArrayBufferView became invalid asynchronously");
+ this->promiseResolver->reject(exception);
+ return;
+ }
+ memcpy(this->destinationDataPtr, this->shmReadbackResultData,
esprehn 2016/10/18 01:44:16 m_ and all of these should be private
Kai Ninomiya 2016/10/19 17:54:11 Done.
+ this->destinationByteLength);
+ // TODO(kainino): What would happen if the DOM was suspended when the
+ // promise became resolved? Could another JS task happen between the memcpy
+ // and the promise resolution task, which would see the wrong data?
+ this->promiseResolver->resolve(this->destinationArrayBufferView);
+
+ context->contextGL()->DeleteQueriesEXT(1, &commandsIssuedQueryID);
+ this->destroy();
+ this->context->unregisterGetBufferSubDataAsyncCallback(this);
+ }
+
+ private:
+ WeakPersistent<WebGL2RenderingContextBase> context;
+ Persistent<ScriptPromiseResolver> promiseResolver;
+
+ // Pointer to shared memory where the gpu readback result is stored.
+ void* shmReadbackResultData;
+ // ID of the GL query used to call this callback.
+ GLuint commandsIssuedQueryID;
+
+ // ArrayBufferView returned from the promise.
+ Persistent<DOMArrayBufferView> destinationArrayBufferView;
+ // Pointer into the offset into destinationArrayBufferView.
+ void* destinationDataPtr;
esprehn 2016/10/18 01:44:16 m_ and remove this-> on all accesses
Kai Ninomiya 2016/10/19 17:54:11 Done.
+ // Size in bytes of the copy operation being performed.
+ long long destinationByteLength;
+};
+
WebGL2RenderingContextBase::WebGL2RenderingContextBase(
HTMLCanvasElement* passedCanvas,
std::unique_ptr<WebGraphicsContext3DProvider> contextProvider,
@@ -187,6 +264,15 @@ WebGL2RenderingContextBase::~WebGL2RenderingContextBase() {
m_currentTransformFeedbackPrimitivesWrittenQuery = nullptr;
}
+void WebGL2RenderingContextBase::destroyContext() {
+ for (auto& callback : m_getBufferSubDataAsyncCallbacks) {
+ callback->destroy();
+ }
+ m_getBufferSubDataAsyncCallbacks.clear();
+
+ WebGLRenderingContextBase::destroyContext();
+}
+
void WebGL2RenderingContextBase::initializeNewContext() {
ASSERT(!isContextLost());
ASSERT(drawingBuffer());
@@ -357,38 +443,104 @@ void WebGL2RenderingContextBase::getBufferSubData(GLenum target,
DOMArrayBufferView* dstData,
GLuint dstOffset,
GLuint length) {
- const char* funcName = "getBufferSubData";
- if (isContextLost())
- return;
- if (!validateValueFitNonNegInt32(funcName, "srcByteOffset", srcByteOffset)) {
+ WebGLBuffer* sourceBuffer = nullptr;
+ void* destinationDataPtr = nullptr;
+ long long destinationByteLength = 0;
+ const char* message = validateGetBufferSubData(
+ __FUNCTION__, target, srcByteOffset, dstData, dstOffset, length,
+ &sourceBuffer, &destinationDataPtr, &destinationByteLength);
+ if (message) {
return;
}
- WebGLBuffer* buffer = validateBufferDataTarget(funcName, target);
- if (!buffer)
- return;
- void* subBaseAddress = nullptr;
- long long subByteLength = 0;
- if (!validateSubSourceAndGetData(dstData, dstOffset, length, &subBaseAddress,
- &subByteLength)) {
- synthesizeGLError(GL_INVALID_VALUE, funcName, "buffer overflow");
- return;
- }
- if (subByteLength == 0) {
+
+ // If the length of the copy is zero, this is a no-op.
+ if (!destinationByteLength) {
return;
}
void* mappedData =
contextGL()->MapBufferRange(target, static_cast<GLintptr>(srcByteOffset),
- subByteLength, GL_MAP_READ_BIT);
+ destinationByteLength, GL_MAP_READ_BIT);
if (!mappedData)
return;
- memcpy(subBaseAddress, mappedData, subByteLength);
+ memcpy(destinationDataPtr, mappedData, destinationByteLength);
contextGL()->UnmapBuffer(target);
}
+ScriptPromise WebGL2RenderingContextBase::getBufferSubDataAsync(
+ ScriptState* scriptState,
+ GLenum target,
+ GLintptr srcByteOffset,
+ DOMArrayBufferView* dstData,
+ GLuint dstOffset,
+ GLuint length) {
+ ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
+ ScriptPromise promise = resolver->promise();
+
+ WebGLBuffer* sourceBuffer = nullptr;
+ void* destinationDataPtr = nullptr;
+ long long destinationByteLength = 0;
+ const char* message = validateGetBufferSubData(
+ __FUNCTION__, target, srcByteOffset, dstData, dstOffset, length,
+ &sourceBuffer, &destinationDataPtr, &destinationByteLength);
+ if (message) {
+ DOMException* exception = DOMException::create(InvalidStateError, message);
+ resolver->reject(exception);
+ return promise;
+ }
+
+ message = validateGetBufferSubDataBounds(
+ __FUNCTION__, sourceBuffer, srcByteOffset, destinationByteLength);
+ if (message) {
+ DOMException* exception = DOMException::create(InvalidStateError, message);
+ resolver->reject(exception);
+ return promise;
+ }
+
+ // If the length of the copy is zero, this is a no-op.
+ if (!destinationByteLength) {
+ resolver->resolve(dstData);
+ return promise;
+ }
+
+ GLuint queryID;
+ contextGL()->GenQueriesEXT(1, &queryID);
+ contextGL()->BeginQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM, queryID);
+ void* mappedData = contextGL()->GetBufferSubDataAsyncCHROMIUM(
+ target, srcByteOffset, destinationByteLength);
+ contextGL()->EndQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM);
+ if (!mappedData) {
+ DOMException* exception =
+ DOMException::create(InvalidStateError, "Out of memory");
+ resolver->reject(exception);
+ return promise;
+ }
+
+ auto callbackObject = adoptRef(new WebGLGetBufferSubDataAsyncCallback(
+ this, resolver, mappedData, queryID, dstData, destinationDataPtr,
+ destinationByteLength));
+ registerGetBufferSubDataAsyncCallback(callbackObject.get());
+ auto callback =
+ WTF::bind(&WebGLGetBufferSubDataAsyncCallback::resolve, callbackObject);
+ drawingBuffer()->contextProvider()->signalQuery(
+ queryID, convertToBaseCallback(std::move(callback)));
+
+ return promise;
+}
+
+void WebGL2RenderingContextBase::registerGetBufferSubDataAsyncCallback(
+ WebGLGetBufferSubDataAsyncCallback* callback) {
+ m_getBufferSubDataAsyncCallbacks.insert(callback);
+}
+
+void WebGL2RenderingContextBase::unregisterGetBufferSubDataAsyncCallback(
+ WebGLGetBufferSubDataAsyncCallback* callback) {
+ m_getBufferSubDataAsyncCallbacks.erase(callback);
+}
+
void WebGL2RenderingContextBase::blitFramebuffer(GLint srcX0,
GLint srcY0,
GLint srcX1,
@@ -4419,6 +4571,63 @@ bool WebGL2RenderingContextBase::validateBufferDataUsage(
}
}
+const char* WebGL2RenderingContextBase::validateGetBufferSubData(
+ const char* functionName,
+ GLenum target,
+ GLintptr sourceByteOffset,
+ DOMArrayBufferView* destinationArrayBufferView,
+ GLuint destinationOffset,
+ GLuint length,
+ WebGLBuffer** outSourceBuffer,
+ void** outDestinationDataPtr,
+ long long* outDestinationByteLength) {
+ if (isContextLost()) {
+ return "Context lost";
+ }
+
+ if (!validateValueFitNonNegInt32(functionName, "srcByteOffset",
+ sourceByteOffset)) {
+ return "Invalid value: srcByteOffset";
+ }
+ if (target == GL_TRANSFORM_FEEDBACK_BUFFER && m_currentProgram &&
+ m_currentProgram->activeTransformFeedbackCount()) {
+ synthesizeGLError(GL_INVALID_OPERATION, functionName,
+ "targeted transform feedback buffer is active");
+ return "Invalid operation: targeted transform feedback buffer is active";
+ }
+
+ WebGLBuffer* sourceBuffer = validateBufferDataTarget(functionName, target);
+ if (!sourceBuffer) {
+ return "Invalid operation: no buffer bound to target";
+ }
+ *outSourceBuffer = sourceBuffer;
+
+ if (!validateSubSourceAndGetData(
+ destinationArrayBufferView, destinationOffset, length,
+ outDestinationDataPtr, outDestinationByteLength)) {
+ synthesizeGLError(GL_INVALID_VALUE, functionName, "overflow of dstData");
+ return "Invalid value: overflow of dstData";
+ }
+
+ return nullptr;
+}
+
+const char* WebGL2RenderingContextBase::validateGetBufferSubDataBounds(
+ const char* functionName,
+ WebGLBuffer* sourceBuffer,
+ GLintptr srcByteOffset,
+ long long subByteLength) {
+ CheckedNumeric<long long> srcEnd = srcByteOffset;
+ srcEnd += subByteLength;
+ if (!srcEnd.IsValid() || srcEnd.ValueOrDie() > sourceBuffer->getSize()) {
+ synthesizeGLError(GL_INVALID_VALUE, functionName,
+ "overflow of bound buffer");
+ return "Invalid value: overflow of bound buffer";
+ }
+
+ return nullptr;
+}
+
void WebGL2RenderingContextBase::removeBoundBuffer(WebGLBuffer* buffer) {
if (m_boundCopyReadBuffer == buffer)
m_boundCopyReadBuffer = nullptr;

Powered by Google App Engine
This is Rietveld 408576698