String GraphicsContext3DInternal::getShaderInfoLog(Platform3DObject shader) { LOGWEBGL("getShaderInfoLog()"); HashMap<Platform3DObject, ShaderSourceEntry>::iterator result = m_shaderSourceMap.find(shader); if (result == m_shaderSourceMap.end()) { LOGWEBGL(" shader not found"); return ""; } ShaderSourceEntry entry = result->second; if (entry.isValid) { LOGWEBGL(" validated shader, retrieve OpenGL log"); GLuint shaderID = shader; GLint logLength; glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &logLength); if (!logLength) return ""; char* log = 0; if ((log = (char *)fastMalloc(logLength * sizeof(char))) == 0) return ""; GLsizei returnedLogLength; glGetShaderInfoLog(shaderID, logLength, &returnedLogLength, log); String res = String(log, returnedLogLength); fastFree(log); return res; } else { LOGWEBGL(" non-validated shader, use ANGLE log"); return entry.log; } }
bool GraphicsContext3DInternal::lockFrontBuffer(T& image, SkRect& rect) { LOGWEBGL("GraphicsContext3DInternal::lockFrontBuffer()"); MutexLocker lock(m_fboMutex); FBO* fbo = m_frontFBO; if (!fbo || !fbo->image()) { LOGWEBGL("-GraphicsContext3DInternal::lockFrontBuffer(), fbo = %p", fbo); return false; } fbo->setLocked(true); image = (T)(fbo->image()); RenderObject* renderer = m_canvas->renderer(); if (renderer && renderer->isBox()) { RenderBox* box = (RenderBox*)renderer; rect.setXYWH(box->borderLeft() + box->paddingLeft(), box->borderTop() + box->paddingTop(), box->contentWidth(), box->contentHeight()); } return true; }
PassRefPtr<ImageData> GraphicsContext3DInternal::paintRenderingResultsToImageData() { LOGWEBGL("paintRenderingResultsToImageData()"); // Reading premultiplied alpha would involve unpremultiplying, which is lossy. if (m_attrs.premultipliedAlpha) return 0; RefPtr<ImageData> imageData = ImageData::create(IntSize(m_width, m_height)); unsigned char* pixels = imageData->data()->data()->data(); //[CAPPFIX_WEB_WEBGL] - better performance if (m_canvasDirty) { glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); } else { LOGWEBGL("paintRenderingResultsToImageData() >>> from clean canvas, so read the back buffer >>> m_canvasDirty:%d", m_canvasDirty); unsigned char* bits; MutexLocker lock(m_fboMutex); FBO* fbo = m_frontFBO; if (!fbo) return NULL; if (fbo->lockGraphicBuffer((void**)&bits)) { memcpy(pixels, bits, (m_width * m_height * 4)); fbo->unlockGraphicBuffer(); } } LOGWEBGL("-paintRenderingResultsToImageData()"); // [CAPPFIX_WEB_WEBGL_END] return imageData; }
void GraphicsContext3DInternal::runSyncThread() { LOGWEBGL("SyncThread: starting"); FBO* fbo = 0; MutexLocker lock(m_threadMutex); m_threadState = THREAD_STATE_RUN; // Signal to creator that we are up and running m_threadCondition.broadcast(); while (m_threadState == THREAD_STATE_RUN) { while (m_threadState == THREAD_STATE_RUN) { { MutexLocker lock(m_fboMutex); if (!m_queuedBuffers.isEmpty()) { fbo = m_queuedBuffers.takeFirst(); break; } } m_threadCondition.wait(m_threadMutex); } LOGWEBGL("SyncThread: woke after waiting for FBO, fbo = %p", fbo); if (m_threadState != THREAD_STATE_RUN) break; //[CAPPFIX_WEB_WEBGL] - Improve UI Response Begin if (m_needImproveUIResponseMode) glFinish(); //[CAPPFIX_WEB_WEBGL_END] destroyEGLSync(fbo); { MutexLocker lock(m_fboMutex); m_preparedBuffers.append(fbo); LOGWEBGL("SyncThread: prepared buffer = %p", fbo); //[CAPPFIX_WEB_WEBGL] - Improve UI Response Begin if (m_needImproveUIResponseMode) { // Wait at least one sec for buffer to get used. // This prevents the webkit thread running ahead of itself m_fboCondition.timedWait(m_fboMutex, currentTime() + 0.3); } //[CAPPFIX_WEB_WEBGL_END] updateFrontBuffer(); } // Invalidate the canvas region if (m_postInvalidate) { JNIEnv* env = JSC::Bindings::getJNIEnv(); env->CallVoidMethod(m_webView, m_postInvalidate); } } // Signal to calling thread that we have stopped m_threadState = THREAD_STATE_STOPPED; m_threadCondition.broadcast(); LOGWEBGL("SyncThread: terminating"); }
WebGLLayer::~WebGLLayer() { LOGWEBGL("WebGLLayer::~WebGLLayer(), this = %p", this); for (int i = 0; i < 2; i++) { if (m_textures[i] != 0) { LOGWEBGL("glDeleteTextures(1, %d)", m_textures[i]); glDeleteTextures(1, &m_textures[i]); LOGWEBGL("glGetError() = %d", glGetError()); } } }
bool WebGLLayer::drawGL(bool layerTilesDisabled) { LOGWEBGL("WebGLLayer::drawGL() [+]"); bool askScreenUpdate = false; askScreenUpdate |= LayerAndroid::drawGL(layerTilesDisabled); if (m_proxy.get()) { EGLImageKHR eglImage; int width; int height; SkRect localBounds; bool requestUpdate; bool locked = m_proxy->lockFrontBuffer(eglImage, width, height, localBounds, requestUpdate); if (locked) { GLuint texture; LOGWEBGL("WebGLLayer::drawGL(), this = %p, m_proxy = %p, eglImage = %d", this, m_proxy.get(), eglImage); if (m_eglImages[0] == eglImage) { texture = m_textures[0]; } else if (m_eglImages[1] == eglImage) { texture = m_textures[1]; } else { // New buffer int idx = 0; if (m_eglImages[idx] != 0) idx++; if (m_eglImages[idx] != 0) return false; m_eglImages[idx] = eglImage; m_textures[idx] = createTexture(eglImage, width, height); texture = m_textures[idx]; } // Flip the y-coordinate TransformationMatrix transform = m_drawTransform; transform = transform.translate(0, 2 * localBounds.top() + localBounds.height()); transform = transform.scale3d(1.0, -1.0, 1.0); TextureQuadData data(texture, GL_TEXTURE_2D, GL_LINEAR, LayerQuad, &transform, &localBounds, 1.0f, true); TilesManager::instance()->shader()->drawQuad(&data); m_proxy->releaseFrontBuffer(); askScreenUpdate |= requestUpdate; } } else { LOGWEBGL("m_proxy.get() returned NULL"); } return askScreenUpdate; }
GLint GraphicsContext3DInternal::checkGLError(const char* s) { GLint error = glGetError(); if (error == GL_NO_ERROR) { LOGWEBGL("%s() OK", s); } else { LOGWEBGL("after %s() glError (0x%x)", s, error); } return error; }
void GraphicsContext3DInternal::startSyncThread() { LOGWEBGL("+startSyncThread()"); MutexLocker lock(m_threadMutex); m_threadState = THREAD_STATE_STOPPED; m_syncThread = createThread(syncThreadStart, this, "GraphicsContext3DInternal"); // Wait for thread to start while (m_threadState != THREAD_STATE_RUN) { m_threadCondition.wait(m_threadMutex); } LOGWEBGL("-startSyncThread()"); }
bool GraphicsContext3DInternal::initEGL() { m_dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (m_dpy == EGL_NO_DISPLAY) return false; EGLint majorVersion; EGLint minorVersion; EGLBoolean returnValue = eglInitialize(m_dpy, &majorVersion, &minorVersion); if (returnValue != EGL_TRUE) return false; LOGWEBGL("EGL version %d.%d", majorVersion, minorVersion); const char *s = eglQueryString(m_dpy, EGL_VENDOR); LOGWEBGL("EGL_VENDOR = %s", s); s = eglQueryString(m_dpy, EGL_VERSION); LOGWEBGL("EGL_VERSION = %s", s); s = eglQueryString(m_dpy, EGL_EXTENSIONS); LOGWEBGL("EGL_EXTENSIONS = %s", s); s = eglQueryString(m_dpy, EGL_CLIENT_APIS); LOGWEBGL("EGL_CLIENT_APIS = %s", s); EGLint config_attribs[21]; int p = 0; config_attribs[p++] = EGL_BLUE_SIZE; config_attribs[p++] = 8; config_attribs[p++] = EGL_GREEN_SIZE; config_attribs[p++] = 8; config_attribs[p++] = EGL_RED_SIZE; config_attribs[p++] = 8; config_attribs[p++] = EGL_SURFACE_TYPE; config_attribs[p++] = EGL_PBUFFER_BIT; config_attribs[p++] = EGL_RENDERABLE_TYPE; config_attribs[p++] = EGL_OPENGL_ES2_BIT; config_attribs[p++] = EGL_ALPHA_SIZE; config_attribs[p++] = m_attrs.alpha ? 8 : 0; if (m_attrs.depth) { config_attribs[p++] = EGL_DEPTH_SIZE; config_attribs[p++] = 16; } if (m_attrs.stencil) { config_attribs[p++] = EGL_STENCIL_SIZE; config_attribs[p++] = 8; } // Antialiasing currently is not supported. m_attrs.antialias = false; config_attribs[p] = EGL_NONE; EGLint num_configs = 0; return (eglChooseConfig(m_dpy, config_attribs, &m_config, 1, &num_configs) == EGL_TRUE); }
bool GraphicsContext3DInternal::paintCompositedResultsToCanvas(CanvasRenderingContext* context) { LOGWEBGL("paintCompositedResultsToCanvas()"); ImageBuffer* imageBuffer = context->canvas()->buffer(); const SkBitmap& canvasBitmap = imageBuffer->context()->platformContext()->recordingCanvas()->getDevice()->accessBitmap(false); SkCanvas canvas(canvasBitmap); MutexLocker lock(m_fboMutex); FBO* fbo = m_frontFBO; if (!fbo) return false; SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, m_width, m_height, fbo->bytesPerRow()); unsigned char* bits = NULL; if (fbo->lockGraphicBuffer((void**)&bits)) { bitmap.setPixels(bits); SkRect dstRect; dstRect.iset(0, 0, imageBuffer->size().width(), imageBuffer->size().height()); canvas.save(); canvas.translate(0, SkIntToScalar(imageBuffer->size().height())); canvas.scale(SK_Scalar1, -SK_Scalar1); canvas.drawBitmapRect(bitmap, 0, dstRect); canvas.restore(); bitmap.setPixels(0); fbo->unlockGraphicBuffer(); } return true; }
void GraphicsContext3DInternal::reshape(int width, int height) { LOGWEBGL("reshape(%d, %d)", width, height); bool mustRestoreFBO = (m_boundFBO != (m_currentFBO ? m_currentFBO->fbo() : 0)); m_width = width > m_maxwidth ? m_maxwidth : width; m_height = height > m_maxheight ? m_maxheight : height; stopSyncThread(); makeContextCurrent(); m_proxy->setGraphicsContext(0); { MutexLocker lock(m_fboMutex); deleteContext(false); if (createContext(false)) { if (!mustRestoreFBO) { m_boundFBO = m_currentFBO->fbo(); } glBindFramebuffer(GL_FRAMEBUFFER, m_boundFBO); } } m_proxy->setGraphicsContext(this); startSyncThread(); }
void GraphicsContext3DInternal::deleteContext(bool deleteEGLContext) { LOGWEBGL("deleteContext(%s)", deleteEGLContext ? "true" : "false"); makeContextCurrent(); glBindFramebuffer(GL_FRAMEBUFFER, 0); m_freeBuffers.clear(); m_queuedBuffers.clear(); m_preparedBuffers.clear(); //[CAPPFIX_WEB_WEBGL] - Handle FBO creation failure for (int i = 0; i < m_nfbo; i++) { if (m_fbo[i]) { delete m_fbo[i]; m_fbo[i] = 0; } } m_nfbo=0; //[CAPPFIX_WEB_WEBGL_END] m_currentFBO = 0; m_frontFBO = 0; eglMakeCurrent(m_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if (deleteEGLContext) { if (m_surface != EGL_NO_SURFACE) { eglDestroySurface(m_dpy, m_surface); m_surface = EGL_NO_SURFACE; } if (m_context != EGL_NO_CONTEXT) { eglDestroyContext(m_dpy, m_context); m_context = EGL_NO_CONTEXT; } } }
GLuint FBO::createTexture(EGLImageKHR image, int width, int height) { LOGWEBGL("createTexture(image = %p)", image); GLuint texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); bool error = false; if (image) { glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image); error = (GraphicsContext3DInternal::checkGLError("glEGLImageTargetTexture2DOES") != GL_NO_ERROR); } else { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); error = (GraphicsContext3DInternal::checkGLError("glTexImage2D()") != GL_NO_ERROR); } glBindTexture(GL_TEXTURE_2D, 0); if (error) { glDeleteTextures(1, &texture); texture = 0; } return texture; }
void GraphicsContext3DInternal::stopSyncThread() { LOGWEBGL("+stopSyncThread()"); MutexLocker lock(m_threadMutex); if (m_syncThread) { m_threadState = THREAD_STATE_STOP; // Signal thread to wake up m_threadCondition.broadcast(); // Wait for thread to stop while (m_threadState != THREAD_STATE_STOPPED) { m_threadCondition.wait(m_threadMutex); } m_syncThread = 0; } LOGWEBGL("-stopSyncThread()"); }
bool GraphicsContext3DInternal::createContext(bool createEGLContext) { LOGWEBGL("createContext()"); if (createEGLContext) { const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; const EGLint surface_attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; m_surface = eglCreatePbufferSurface(m_dpy, m_config, surface_attribs); EGLContext context = EGL_NO_CONTEXT; #if USE(SHARED_TEXTURE_WEBGL) context = TilesManager::instance()->getEglContext(); #endif m_context = eglCreateContext(m_dpy, m_config, context, context_attribs); } if (m_context == EGL_NO_CONTEXT) { deleteContext(createEGLContext); return false; } makeContextCurrent(); for (int i = 0; i < NUM_BUFFERS; i++) { FBO* tmp = FBO::createFBO(m_dpy, m_width > 0 ? m_width : 1, m_height > 0 ? m_height : 1, m_attrs); if (tmp == 0) { LOGWEBGL("Failed to create FBO"); deleteContext(createEGLContext); return false; } m_fbo[i] = tmp; //[CAPPFIX_WEB_WEBGL] - Handle FBO creation failure m_nfbo++; //[CAPPFIX_WEB_WEBGL_END] m_freeBuffers.append(tmp); } m_currentFBO = dequeueBuffer(); m_boundFBO = m_currentFBO->fbo(); m_frontFBO = 0; glBindFramebuffer(GL_FRAMEBUFFER, m_boundFBO); return true; }
//[CAPPFIX_WEB_WEBGL] - Improve UI Response Begin long GraphicsContext3DInternal::logTickCount(const CString string, const long time, const long min) { const long dtime = getTickCount() - time; if (dtime > min/*(msec)*/) { LOGWEBGL("[%s] %d ms", string.data(), dtime); } return dtime; }
void GraphicsContext3DInternal::shaderSource(Platform3DObject shader, const String& string) { LOGWEBGL("shaderSource()"); ShaderSourceEntry entry; entry.source = string; m_shaderSourceMap.set(shader, entry); }
void GraphicsContext3DInternal::recreateSurface() { LOGWEBGL("recreateSurface()"); if (m_currentFBO != 0) // We already have a current surface return; reshape(m_width, m_height); glViewport(m_savedViewport.x, m_savedViewport.y, m_savedViewport.width, m_savedViewport.height); }
void GraphicsContext3DInternal::viewport(long x, long y, unsigned long width, unsigned long height) { LOGWEBGL("glViewport(%d, %d, %d, %d)", x, y, width, height); glViewport(x, y, width, height); m_savedViewport.x = x; m_savedViewport.y = y; m_savedViewport.width = width; m_savedViewport.height = height; }
WebGLLayer::WebGLLayer(const WebGLLayer& layer) : LayerAndroid(layer) , m_proxy(layer.m_proxy) { LOGWEBGL("WebGLLayer::WebGLLayer(const WebGLLayer& %p), this = %p", &layer, this); for (int i = 0; i < 2; i++) { m_eglImages[i] = layer.m_eglImages[i]; m_textures[i] = layer.m_textures[i]; } }
WebGLLayer::WebGLLayer(GraphicsContext3DProxy* proxy) : LayerAndroid((RenderLayer*)0) , m_proxy(proxy) { LOGWEBGL("WebGLLayer::WebGLLayer(GraphicsContext3DProxy* %p), this = %p", proxy, this); for (int i = 0; i < 2; i++) { m_eglImages[i] = 0; m_textures[i] = 0; } }
String GraphicsContext3DInternal::getShaderSource(Platform3DObject shader) { LOGWEBGL("getShaderSource()"); HashMap<Platform3DObject, ShaderSourceEntry>::iterator result = m_shaderSourceMap.find(shader); if (result == m_shaderSourceMap.end()) return ""; return result->second.source; }
void GraphicsContext3DInternal::bindFramebuffer(GC3Denum target, Platform3DObject buffer) { LOGWEBGL("glBindFrameBuffer(%d, %d)", target, buffer); makeContextCurrent(); MutexLocker lock(m_fboMutex); if (!buffer && m_currentFBO) { buffer = m_currentFBO->fbo(); } glBindFramebuffer(target, buffer); m_boundFBO = buffer; }
/* * Must hold m_fboMutex when calling this function. */ FBO* GraphicsContext3DInternal::dequeueBuffer() { LOGWEBGL("GraphicsContext3DInternal::dequeueBuffer()"); while (m_freeBuffers.isEmpty()) { m_fboCondition.wait(m_fboMutex); } FBO* fbo = m_freeBuffers.takeFirst(); destroyEGLSync(fbo); return fbo; }
FBO* FBO::createFBO(EGLDisplay dpy, int width, int height, GraphicsContext3D::Attributes attributes) { LOGWEBGL("createFBO()"); FBO* fbo = new FBO(dpy); if (!fbo->init(width, height, attributes)) { delete fbo; return 0; } return fbo; }
unsigned long GraphicsContext3DInternal::getError() { if (m_syntheticErrors.size() > 0) { ListHashSet<unsigned long>::iterator iter = m_syntheticErrors.begin(); unsigned long err = *iter; m_syntheticErrors.remove(iter); return err; } LOGWEBGL("glGetError()"); makeContextCurrent(); return glGetError(); }
void GraphicsContext3DInternal::compileShader(Platform3DObject shader) { LOGWEBGL("compileShader()"); HashMap<Platform3DObject, ShaderSourceEntry>::iterator result = m_shaderSourceMap.find(shader); if (result == m_shaderSourceMap.end()) { LOGWEBGL(" shader not found"); return; } ShaderSourceEntry& entry = result->second; int shaderType; glGetShaderiv(shader, GL_SHADER_TYPE, &shaderType); ANGLEShaderType ast = shaderType == GL_VERTEX_SHADER ? SHADER_TYPE_VERTEX : SHADER_TYPE_FRAGMENT; String src; String log; bool isValid = m_compiler.validateShaderSource(entry.source.utf8().data(), ast, src, log); entry.log = log; entry.isValid = isValid; if (!isValid) { LOGWEBGL(" shader validation failed"); return; } int len = entry.source.length(); CString cstr = entry.source.utf8(); const char* s = cstr.data(); LOGWEBGL("glShaderSource(%s)", cstr.data()); glShaderSource(shader, 1, &s, &len); LOGWEBGL("glCompileShader()"); glCompileShader(shader); }
void GraphicsContext3DInternal::releaseFrontBuffer() { LOGWEBGL("GraphicsContext3DInternal::releaseFrontBuffer()"); MutexLocker lock(m_fboMutex); FBO* fbo = m_frontFBO; if (fbo) { fbo->setLocked(false); if (fbo->sync() != EGL_NO_SYNC_KHR) { eglDestroySyncKHR(m_dpy, fbo->sync()); } fbo->setSync(); } updateFrontBuffer(); }
void GraphicsContext3DInternal::releaseSurface() { LOGWEBGL("releaseSurface(%d)", m_contextId); if (m_currentFBO == 0) // We don't have any current surface return; stopSyncThread(); m_proxy->setGraphicsContext(0); { MutexLocker lock(m_fboMutex); deleteContext(false); } makeContextCurrent(); m_proxy->setGraphicsContext(this); }
FBO::~FBO() { LOGWEBGL("FBO::~FBO()"); if (m_image) { eglDestroyImageKHR(m_dpy, m_image); GraphicsContext3DInternal::checkEGLError("eglDestroyImageKHR"); } if (m_texture) glDeleteTextures(1, &m_texture); if (m_depthBuffer) glDeleteRenderbuffers(1, &m_depthBuffer); if (m_stencilBuffer) glDeleteRenderbuffers(1, &m_stencilBuffer); if (m_fbo) glDeleteFramebuffers(1, &m_fbo); }