/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "WebGL2Context.h" #include "GLContext.h" #include "GLScreenBuffer.h" #include "WebGLContextUtils.h" #include "WebGLFormats.h" #include "WebGLFramebuffer.h" namespace mozilla { void WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { if (IsContextLost()) return; const GLbitfield validBits = LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT; if ((mask | validBits) != validBits) { ErrorInvalidValue("blitFramebuffer: Invalid bit set in mask."); return; } switch (filter) { case LOCAL_GL_NEAREST: case LOCAL_GL_LINEAR: break; default: ErrorInvalidEnumInfo("blitFramebuffer: Bad `filter`:", filter); return; } //// const auto& readFB = mBoundReadFramebuffer; if (readFB && !readFB->ValidateAndInitAttachments("blitFramebuffer's READ_FRAMEBUFFER")) { return; } const auto& drawFB = mBoundDrawFramebuffer; if (drawFB && !drawFB->ValidateAndInitAttachments("blitFramebuffer's DRAW_FRAMEBUFFER")) { return; } //// if (!mBoundReadFramebuffer) { ClearBackbufferIfNeeded(); } WebGLFramebuffer::BlitFramebuffer(this, readFB, srcX0, srcY0, srcX1, srcY1, drawFB, dstX0, dstY0, dstX1, dstY1, mask, filter); } void WebGL2Context::FramebufferTextureLayer(GLenum target, GLenum attachment, WebGLTexture* texture, GLint level, GLint layer) { const char funcName[] = "framebufferTextureLayer"; if (IsContextLost()) return; if (!ValidateFramebufferTarget(target, funcName)) return; WebGLFramebuffer* fb; switch (target) { case LOCAL_GL_FRAMEBUFFER: case LOCAL_GL_DRAW_FRAMEBUFFER: fb = mBoundDrawFramebuffer; break; case LOCAL_GL_READ_FRAMEBUFFER: fb = mBoundReadFramebuffer; break; default: MOZ_CRASH("GFX: Bad target."); } if (!fb) return ErrorInvalidOperation("%s: Cannot modify framebuffer 0.", funcName); fb->FramebufferTextureLayer(funcName, attachment, texture, level, layer); } JS::Value WebGL2Context::GetFramebufferAttachmentParameter(JSContext* cx, GLenum target, GLenum attachment, GLenum pname, ErrorResult& out_error) { return WebGLContext::GetFramebufferAttachmentParameter(cx, target, attachment, pname, out_error); } //// static bool ValidateBackbufferAttachmentEnum(WebGLContext* webgl, const char* funcName, GLenum attachment) { switch (attachment) { case LOCAL_GL_COLOR: case LOCAL_GL_DEPTH: case LOCAL_GL_STENCIL: return true; default: webgl->ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x.", funcName, attachment); return false; } } static bool ValidateFramebufferAttachmentEnum(WebGLContext* webgl, const char* funcName, GLenum attachment) { switch (attachment) { case LOCAL_GL_DEPTH_ATTACHMENT: case LOCAL_GL_STENCIL_ATTACHMENT: case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: return true; } if (attachment < LOCAL_GL_COLOR_ATTACHMENT0) { webgl->ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x.", funcName, attachment); return false; } if (attachment > webgl->LastColorAttachmentEnum()) { // That these errors have different types is ridiculous. webgl->ErrorInvalidOperation("%s: Too-large LOCAL_GL_COLOR_ATTACHMENTn.", funcName); return false; } return true; } bool WebGLContext::ValidateInvalidateFramebuffer(const char* funcName, GLenum target, const dom::Sequence<GLenum>& attachments, ErrorResult* const out_rv, std::vector<GLenum>* const scopedVector, GLsizei* const out_glNumAttachments, const GLenum** const out_glAttachments) { if (IsContextLost()) return false; gl->MakeCurrent(); if (!ValidateFramebufferTarget(target, funcName)) return false; const WebGLFramebuffer* fb; bool isDefaultFB; switch (target) { case LOCAL_GL_FRAMEBUFFER: case LOCAL_GL_DRAW_FRAMEBUFFER: fb = mBoundDrawFramebuffer; isDefaultFB = gl->Screen()->IsDrawFramebufferDefault(); break; case LOCAL_GL_READ_FRAMEBUFFER: fb = mBoundReadFramebuffer; isDefaultFB = gl->Screen()->IsReadFramebufferDefault(); break; default: MOZ_CRASH("GFX: Bad target."); } *out_glNumAttachments = attachments.Length(); *out_glAttachments = attachments.Elements(); if (fb) { for (const auto& attachment : attachments) { if (!ValidateFramebufferAttachmentEnum(this, funcName, attachment)) return false; } } else { for (const auto& attachment : attachments) { if (!ValidateBackbufferAttachmentEnum(this, funcName, attachment)) return false; } if (!isDefaultFB) { MOZ_ASSERT(scopedVector->empty()); scopedVector->reserve(attachments.Length()); for (const auto& attachment : attachments) { switch (attachment) { case LOCAL_GL_COLOR: scopedVector->push_back(LOCAL_GL_COLOR_ATTACHMENT0); break; case LOCAL_GL_DEPTH: scopedVector->push_back(LOCAL_GL_DEPTH_ATTACHMENT); break; case LOCAL_GL_STENCIL: scopedVector->push_back(LOCAL_GL_STENCIL_ATTACHMENT); break; default: MOZ_CRASH(); } } *out_glNumAttachments = scopedVector->size(); *out_glAttachments = scopedVector->data(); } } //// if (!fb) { ClearBackbufferIfNeeded(); // Don't do more validation after these. Invalidate(); mShouldPresent = true; } return true; } void WebGL2Context::InvalidateFramebuffer(GLenum target, const dom::Sequence<GLenum>& attachments, ErrorResult& rv) { const char funcName[] = "invalidateSubFramebuffer"; std::vector<GLenum> scopedVector; GLsizei glNumAttachments; const GLenum* glAttachments; if (!ValidateInvalidateFramebuffer(funcName, target, attachments, &rv, &scopedVector, &glNumAttachments, &glAttachments)) { return; } //// // Some drivers (like OSX 10.9 GL) just don't support invalidate_framebuffer. const bool useFBInvalidation = (mAllowFBInvalidation && gl->IsSupported(gl::GLFeature::invalidate_framebuffer)); if (useFBInvalidation) { gl->fInvalidateFramebuffer(target, glNumAttachments, glAttachments); return; } // Use clear instead? // No-op for now. } void WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::Sequence<GLenum>& attachments, GLint x, GLint y, GLsizei width, GLsizei height, ErrorResult& rv) { const char funcName[] = "invalidateSubFramebuffer"; if (!ValidateNonNegative(funcName, "width", width) || !ValidateNonNegative(funcName, "height", height)) { return; } std::vector<GLenum> scopedVector; GLsizei glNumAttachments; const GLenum* glAttachments; if (!ValidateInvalidateFramebuffer(funcName, target, attachments, &rv, &scopedVector, &glNumAttachments, &glAttachments)) { return; } //// // Some drivers (like OSX 10.9 GL) just don't support invalidate_framebuffer. const bool useFBInvalidation = (mAllowFBInvalidation && gl->IsSupported(gl::GLFeature::invalidate_framebuffer)); if (useFBInvalidation) { gl->fInvalidateSubFramebuffer(target, glNumAttachments, glAttachments, x, y, width, height); return; } // Use clear instead? // No-op for now. } void WebGL2Context::ReadBuffer(GLenum mode) { const char funcName[] = "readBuffer"; if (IsContextLost()) return; if (mBoundReadFramebuffer) { mBoundReadFramebuffer->ReadBuffer(funcName, mode); return; } // Operating on the default framebuffer. if (mode != LOCAL_GL_NONE && mode != LOCAL_GL_BACK) { nsCString enumName; EnumName(mode, &enumName); ErrorInvalidOperation("%s: If READ_FRAMEBUFFER is null, `mode` must be BACK or" " NONE. Was %s.", funcName, enumName.BeginReading()); return; } gl->Screen()->SetReadBuffer(mode); } } // namespace mozilla