Bug 1093967 - Implement BlitFramebuffer. r=kamidphish

This commit is contained in:
Jeff Gilbert 2015-01-12 15:05:21 -08:00
Родитель 18b1606ac8
Коммит 2806be23fd
12 изменённых файлов: 660 добавлений и 128 удалений

Просмотреть файл

@ -4,20 +4,329 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WebGL2Context.h"
#include "GLContext.h"
#include "WebGLContextUtils.h"
using namespace mozilla;
using namespace mozilla::dom;
// Returns one of FLOAT, INT, UNSIGNED_INT.
// Fixed-points (normalized ints) are considered FLOAT.
static GLenum
ValueTypeForFormat(GLenum internalFormat)
{
switch (internalFormat) {
// Fixed-point
case LOCAL_GL_R8:
case LOCAL_GL_RG8:
case LOCAL_GL_RGB565:
case LOCAL_GL_RGB8:
case LOCAL_GL_RGBA4:
case LOCAL_GL_RGB5_A1:
case LOCAL_GL_RGBA8:
case LOCAL_GL_RGB10_A2:
case LOCAL_GL_ALPHA8:
case LOCAL_GL_LUMINANCE8:
case LOCAL_GL_LUMINANCE8_ALPHA8:
case LOCAL_GL_SRGB8:
case LOCAL_GL_SRGB8_ALPHA8:
case LOCAL_GL_R8_SNORM:
case LOCAL_GL_RG8_SNORM:
case LOCAL_GL_RGB8_SNORM:
case LOCAL_GL_RGBA8_SNORM:
// Floating-point
case LOCAL_GL_R16F:
case LOCAL_GL_RG16F:
case LOCAL_GL_RGB16F:
case LOCAL_GL_RGBA16F:
case LOCAL_GL_ALPHA16F_EXT:
case LOCAL_GL_LUMINANCE16F_EXT:
case LOCAL_GL_LUMINANCE_ALPHA16F_EXT:
case LOCAL_GL_R32F:
case LOCAL_GL_RG32F:
case LOCAL_GL_RGB32F:
case LOCAL_GL_RGBA32F:
case LOCAL_GL_ALPHA32F_EXT:
case LOCAL_GL_LUMINANCE32F_EXT:
case LOCAL_GL_LUMINANCE_ALPHA32F_EXT:
case LOCAL_GL_R11F_G11F_B10F:
case LOCAL_GL_RGB9_E5:
return LOCAL_GL_FLOAT;
// Int
case LOCAL_GL_R8I:
case LOCAL_GL_RG8I:
case LOCAL_GL_RGB8I:
case LOCAL_GL_RGBA8I:
case LOCAL_GL_R16I:
case LOCAL_GL_RG16I:
case LOCAL_GL_RGB16I:
case LOCAL_GL_RGBA16I:
case LOCAL_GL_R32I:
case LOCAL_GL_RG32I:
case LOCAL_GL_RGB32I:
case LOCAL_GL_RGBA32I:
return LOCAL_GL_INT;
// Unsigned int
case LOCAL_GL_R8UI:
case LOCAL_GL_RG8UI:
case LOCAL_GL_RGB8UI:
case LOCAL_GL_RGBA8UI:
case LOCAL_GL_R16UI:
case LOCAL_GL_RG16UI:
case LOCAL_GL_RGB16UI:
case LOCAL_GL_RGBA16UI:
case LOCAL_GL_R32UI:
case LOCAL_GL_RG32UI:
case LOCAL_GL_RGB32UI:
case LOCAL_GL_RGBA32UI:
case LOCAL_GL_RGB10_A2UI:
return LOCAL_GL_UNSIGNED_INT;
default:
MOZ_CRASH("Bad `internalFormat`.");
}
}
// -------------------------------------------------------------------------
// Framebuffer objects
static bool
GetFBInfoForBlit(const WebGLFramebuffer* fb, WebGLContext* webgl,
const char* const fbInfo, GLsizei* const out_samples,
GLenum* const out_colorFormat, GLenum* const out_depthFormat,
GLenum* const out_stencilFormat)
{
auto status = fb->PrecheckFramebufferStatus();
if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
webgl->ErrorInvalidOperation("blitFramebuffer: %s is not"
" framebuffer-complete.", fbInfo);
return false;
}
*out_samples = 1; // TODO
if (fb->ColorAttachment(0).IsDefined()) {
const auto& attachement = fb->ColorAttachment(0);
*out_colorFormat = attachement.EffectiveInternalFormat().get();
} else {
*out_colorFormat = 0;
}
if (fb->DepthStencilAttachment().IsDefined()) {
const auto& attachement = fb->DepthStencilAttachment();
*out_depthFormat = attachement.EffectiveInternalFormat().get();
*out_stencilFormat = *out_depthFormat;
} else {
if (fb->DepthAttachment().IsDefined()) {
const auto& attachement = fb->DepthAttachment();
*out_depthFormat = attachement.EffectiveInternalFormat().get();
} else {
*out_depthFormat = 0;
}
if (fb->StencilAttachment().IsDefined()) {
const auto& attachement = fb->StencilAttachment();
*out_stencilFormat = attachement.EffectiveInternalFormat().get();
} else {
*out_stencilFormat = 0;
}
}
return true;
}
void
WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
GLbitfield mask, GLenum filter)
{
MOZ_CRASH("Not Implemented.");
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 GLbitfield depthAndStencilBits = LOCAL_GL_DEPTH_BUFFER_BIT |
LOCAL_GL_STENCIL_BUFFER_BIT;
if (mask & depthAndStencilBits &&
filter != LOCAL_GL_NEAREST)
{
ErrorInvalidOperation("blitFramebuffer: DEPTH_BUFFER_BIT and"
" STENCIL_BUFFER_BIT can only be used with"
" NEAREST filtering.");
return;
}
if (mBoundReadFramebuffer == mBoundDrawFramebuffer) {
// TODO: It's actually more complicated than this. We need to check that
// the underlying buffers are not the same, not the framebuffers
// themselves.
ErrorInvalidOperation("blitFramebuffer: Source and destination must"
" differ.");
return;
}
GLsizei srcSamples;
GLenum srcColorFormat;
GLenum srcDepthFormat;
GLenum srcStencilFormat;
if (mBoundReadFramebuffer) {
if (!GetFBInfoForBlit(mBoundReadFramebuffer, this, "READ_FRAMEBUFFER",
&srcSamples, &srcColorFormat, &srcDepthFormat,
&srcStencilFormat))
{
return;
}
} else {
srcSamples = 1; // Always 1.
// TODO: Don't hardcode these.
srcColorFormat = mOptions.alpha ? LOCAL_GL_RGBA8 : LOCAL_GL_RGB8;
if (mOptions.depth && mOptions.stencil) {
srcDepthFormat = LOCAL_GL_DEPTH24_STENCIL8;
srcStencilFormat = srcDepthFormat;
} else {
if (mOptions.depth) {
srcDepthFormat = LOCAL_GL_DEPTH_COMPONENT16;
}
if (mOptions.stencil) {
srcStencilFormat = LOCAL_GL_STENCIL_INDEX8;
}
}
}
GLsizei dstSamples;
GLenum dstColorFormat;
GLenum dstDepthFormat;
GLenum dstStencilFormat;
if (mBoundDrawFramebuffer) {
if (!GetFBInfoForBlit(mBoundDrawFramebuffer, this, "DRAW_FRAMEBUFFER",
&dstSamples, &dstColorFormat, &dstDepthFormat,
&dstStencilFormat))
{
return;
}
} else {
dstSamples = gl->Screen()->Samples();
// TODO: Don't hardcode these.
dstColorFormat = mOptions.alpha ? LOCAL_GL_RGBA8 : LOCAL_GL_RGB8;
if (mOptions.depth && mOptions.stencil) {
dstDepthFormat = LOCAL_GL_DEPTH24_STENCIL8;
dstStencilFormat = dstDepthFormat;
} else {
if (mOptions.depth) {
dstDepthFormat = LOCAL_GL_DEPTH_COMPONENT16;
}
if (mOptions.stencil) {
dstStencilFormat = LOCAL_GL_STENCIL_INDEX8;
}
}
}
if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
const GLenum srcColorType = srcColorFormat ? ValueTypeForFormat(srcColorFormat)
: 0;
const GLenum dstColorType = dstColorFormat ? ValueTypeForFormat(dstColorFormat)
: 0;
if (dstColorType != srcColorType) {
ErrorInvalidOperation("blitFramebuffer: Color buffer value type"
" mismatch.");
return;
}
const bool srcIsInt = srcColorType == LOCAL_GL_INT ||
srcColorType == LOCAL_GL_UNSIGNED_INT;
if (srcIsInt && filter != LOCAL_GL_NEAREST) {
ErrorInvalidOperation("blitFramebuffer: Integer read buffers can only"
" be filtered with NEAREST.");
return;
}
}
/* GLES 3.0.4, p199:
* Calling BlitFramebuffer will result in an INVALID_OPERATION error if
* mask includes DEPTH_BUFFER_BIT or STENCIL_BUFFER_BIT, and the source
* and destination depth and stencil buffer formats do not match.
*
* jgilbert: The wording is such that if only DEPTH_BUFFER_BIT is specified,
* the stencil formats must match. This seems wrong. It could be a spec bug,
* or I could be missing an interaction in one of the earlier paragraphs.
*/
if (mask & LOCAL_GL_DEPTH_BUFFER_BIT &&
dstDepthFormat != srcDepthFormat)
{
ErrorInvalidOperation("blitFramebuffer: Depth buffer formats must match"
" if selected.");
return;
}
if (mask & LOCAL_GL_STENCIL_BUFFER_BIT &&
dstStencilFormat != srcStencilFormat)
{
ErrorInvalidOperation("blitFramebuffer: Stencil buffer formats must"
" match if selected.");
return;
}
if (dstSamples != 1) {
ErrorInvalidOperation("blitFramebuffer: DRAW_FRAMEBUFFER may not have"
" multiple samples.");
return;
}
if (srcSamples != 1) {
if (mask & LOCAL_GL_COLOR_BUFFER_BIT &&
dstColorFormat != srcColorFormat)
{
ErrorInvalidOperation("blitFramebuffer: Color buffer formats must"
" match if selected, when reading from a"
" multisampled source.");
return;
}
if (dstX0 != srcX0 ||
dstX1 != srcX1 ||
dstY0 != srcY0 ||
dstY1 != srcY1)
{
ErrorInvalidOperation("blitFramebuffer: If the source is"
" multisampled, then the source and dest"
" regions must match exactly.");
return;
}
}
MakeContextCurrent();
gl->fBlitFramebuffer(srcX0, srcY0, srcX1, srcY1,
dstX0, dstY0, dstX1, dstY1,
mask, filter);
}
void
@ -57,16 +366,39 @@ WebGL2Context::InvalidateFramebuffer(GLenum target, const dom::Sequence<GLenum>&
{
if (IsContextLost())
return;
MakeContextCurrent();
if (target != LOCAL_GL_FRAMEBUFFER)
return ErrorInvalidEnumInfo("invalidateFramebuffer: target", target);
for (size_t i = 0; i < attachments.Length(); i++) {
if (!ValidateFramebufferAttachment(attachments[i], "invalidateFramebuffer"))
return;
if (!ValidateFramebufferTarget(target, "framebufferRenderbuffer"))
return;
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("Bad target.");
}
if (!mBoundFramebuffer && !gl->IsDrawingToDefaultFramebuffer()) {
for (size_t i = 0; i < attachments.Length(); i++) {
if (!ValidateFramebufferAttachment(fb, attachments[i],
"invalidateFramebuffer"))
{
return;
}
}
if (!fb && !isDefaultFB) {
dom::Sequence<GLenum> tmpAttachments;
TranslateDefaultAttachments(attachments, &tmpAttachments);
gl->fInvalidateFramebuffer(target, tmpAttachments.Length(), tmpAttachments.Elements());
@ -81,16 +413,39 @@ WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::Sequence<GLenu
{
if (IsContextLost())
return;
MakeContextCurrent();
if (target != LOCAL_GL_FRAMEBUFFER)
return ErrorInvalidEnumInfo("invalidateFramebuffer: target", target);
for (size_t i = 0; i < attachments.Length(); i++) {
if (!ValidateFramebufferAttachment(attachments[i], "invalidateSubFramebuffer"))
return;
if (!ValidateFramebufferTarget(target, "framebufferRenderbuffer"))
return;
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("Bad target.");
}
if (!mBoundFramebuffer && !gl->IsDrawingToDefaultFramebuffer()) {
for (size_t i = 0; i < attachments.Length(); i++) {
if (!ValidateFramebufferAttachment(fb, attachments[i],
"invalidateSubFramebuffer"))
{
return;
}
}
if (!fb && !isDefaultFB) {
dom::Sequence<GLenum> tmpAttachments;
TranslateDefaultAttachments(attachments, &tmpAttachments);
gl->fInvalidateSubFramebuffer(target, tmpAttachments.Length(), tmpAttachments.Elements(),

Просмотреть файл

@ -330,7 +330,8 @@ WebGLContext::DestroyResourcesAndContext()
mBoundTransformFeedbackBuffer = nullptr;
mBoundUniformBuffer = nullptr;
mCurrentProgram = nullptr;
mBoundFramebuffer = nullptr;
mBoundDrawFramebuffer = nullptr;
mBoundReadFramebuffer = nullptr;
mActiveOcclusionQuery = nullptr;
mBoundRenderbuffer = nullptr;
mBoundVertexArray = nullptr;
@ -1887,7 +1888,8 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLContext,
mBoundTransformFeedbackBuffer,
mBoundUniformBuffer,
mCurrentProgram,
mBoundFramebuffer,
mBoundDrawFramebuffer,
mBoundReadFramebuffer,
mBoundRenderbuffer,
mBoundVertexArray,
mDefaultVertexArray,

Просмотреть файл

@ -312,7 +312,8 @@ public:
uint32_t Generation() { return mGeneration.value(); }
// Returns null if the current bound FB is not likely complete.
const WebGLRectangleObject* CurValidFBRectObject() const;
const WebGLRectangleObject* CurValidDrawFBRectObject() const;
const WebGLRectangleObject* CurValidReadFBRectObject() const;
static const size_t kMaxColorAttachments = 16;
@ -415,7 +416,8 @@ public:
GLint level);
// Framebuffer validation
bool ValidateFramebufferAttachment(GLenum attachment, const char* funcName);
bool ValidateFramebufferAttachment(const WebGLFramebuffer* fb,
GLenum attachment, const char* funcName);
void FrontFace(GLenum mode);
void GenerateMipmap(GLenum target);
@ -1404,7 +1406,10 @@ protected:
uint32_t mMaxFramebufferColorAttachments;
WebGLRefPtr<WebGLFramebuffer> mBoundFramebuffer;
bool ValidateFramebufferTarget(GLenum target, const char* const info);
WebGLRefPtr<WebGLFramebuffer> mBoundDrawFramebuffer;
WebGLRefPtr<WebGLFramebuffer> mBoundReadFramebuffer;
WebGLRefPtr<WebGLRenderbuffer> mBoundRenderbuffer;
WebGLRefPtr<WebGLTransformFeedback> mBoundTransformFeedback;
WebGLRefPtr<WebGLVertexArray> mBoundVertexArray;

Просмотреть файл

@ -99,8 +99,8 @@ bool WebGLContext::DrawArrays_check(GLint first, GLsizei count, GLsizei primcoun
MakeContextCurrent();
if (mBoundFramebuffer) {
if (!mBoundFramebuffer->CheckAndInitializeAttachments()) {
if (mBoundDrawFramebuffer) {
if (!mBoundDrawFramebuffer->CheckAndInitializeAttachments()) {
ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
return false;
}
@ -280,8 +280,8 @@ WebGLContext::DrawElements_check(GLsizei count, GLenum type,
MakeContextCurrent();
if (mBoundFramebuffer) {
if (!mBoundFramebuffer->CheckAndInitializeAttachments()) {
if (mBoundDrawFramebuffer) {
if (!mBoundDrawFramebuffer->CheckAndInitializeAttachments()) {
ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
return false;
}
@ -370,7 +370,7 @@ void WebGLContext::Draw_cleanup()
UndoFakeVertexAttrib0();
UnbindFakeBlackTextures();
if (!mBoundFramebuffer) {
if (!mBoundDrawFramebuffer) {
Invalidate();
mShouldPresent = true;
MOZ_ASSERT(!mBackbufferNeedsClear);
@ -388,7 +388,7 @@ void WebGLContext::Draw_cleanup()
}
// Let's check the viewport
const WebGLRectangleObject* rect = CurValidFBRectObject();
const WebGLRectangleObject* rect = CurValidDrawFBRectObject();
if (rect) {
if (mViewportWidth > rect->Width() ||
mViewportHeight > rect->Height())

Просмотреть файл

@ -29,8 +29,8 @@ WebGLContext::Clear(GLbitfield mask)
GenerateWarning("Calling gl.clear() with RASTERIZER_DISCARD enabled has no effects.");
}
if (mBoundFramebuffer) {
if (!mBoundFramebuffer->CheckAndInitializeAttachments())
if (mBoundDrawFramebuffer) {
if (!mBoundDrawFramebuffer->CheckAndInitializeAttachments())
return ErrorInvalidFramebufferOperation("clear: incomplete framebuffer");
gl->fClear(mask);
@ -131,12 +131,11 @@ WebGLContext::DrawBuffers(const dom::Sequence<GLenum>& buffers)
const size_t buffersLength = buffers.Length();
if (buffersLength == 0) {
if (!buffersLength) {
return ErrorInvalidValue("drawBuffers: invalid <buffers> (buffers must not be empty)");
}
if (mBoundFramebuffer == 0)
{
if (!mBoundDrawFramebuffer) {
// OK: we are rendering in the default framebuffer
/* EXT_draw_buffers :

Просмотреть файл

@ -58,24 +58,37 @@ using namespace mozilla::gfx;
static bool BaseTypeAndSizeFromUniformType(GLenum uType, GLenum* baseType,
GLint* unitSize);
const WebGLRectangleObject*
WebGLContext::CurValidFBRectObject() const
static const WebGLRectangleObject*
CurValidFBRectObject(const WebGLContext* webgl,
const WebGLFramebuffer* boundFB)
{
const WebGLRectangleObject* rect = nullptr;
if (mBoundFramebuffer) {
if (boundFB) {
// We don't really need to ask the driver.
// Use 'precheck' to just check that our internal state looks good.
FBStatus precheckStatus = mBoundFramebuffer->PrecheckFramebufferStatus();
FBStatus precheckStatus = boundFB->PrecheckFramebufferStatus();
if (precheckStatus == LOCAL_GL_FRAMEBUFFER_COMPLETE)
rect = &mBoundFramebuffer->RectangleObject();
rect = &boundFB->RectangleObject();
} else {
rect = static_cast<const WebGLRectangleObject*>(this);
rect = static_cast<const WebGLRectangleObject*>(webgl);
}
return rect;
}
const WebGLRectangleObject*
WebGLContext::CurValidDrawFBRectObject() const
{
return CurValidFBRectObject(this, mBoundDrawFramebuffer);
}
const WebGLRectangleObject*
WebGLContext::CurValidReadFBRectObject() const
{
return CurValidFBRectObject(this, mBoundReadFramebuffer);
}
//
// WebGL API
//
@ -164,8 +177,8 @@ WebGLContext::BindFramebuffer(GLenum target, WebGLFramebuffer* wfb)
if (IsContextLost())
return;
if (target != LOCAL_GL_FRAMEBUFFER)
return ErrorInvalidEnum("bindFramebuffer: target must be GL_FRAMEBUFFER");
if (!ValidateFramebufferTarget(target, "bindFramebuffer"))
return;
if (!ValidateObjectAllowDeletedOrNull("bindFramebuffer", wfb))
return;
@ -184,7 +197,20 @@ WebGLContext::BindFramebuffer(GLenum target, WebGLFramebuffer* wfb)
gl->fBindFramebuffer(target, framebuffername);
}
mBoundFramebuffer = wfb;
switch (target) {
case LOCAL_GL_FRAMEBUFFER:
mBoundDrawFramebuffer = wfb;
mBoundReadFramebuffer = wfb;
break;
case LOCAL_GL_DRAW_FRAMEBUFFER:
mBoundDrawFramebuffer = wfb;
break;
case LOCAL_GL_READ_FRAMEBUFFER:
mBoundReadFramebuffer = wfb;
break;
default:
break;
}
}
void
@ -351,30 +377,38 @@ WebGLContext::CheckFramebufferStatus(GLenum target)
if (IsContextLost())
return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
if (target != LOCAL_GL_FRAMEBUFFER) {
ErrorInvalidEnum("checkFramebufferStatus: target must be FRAMEBUFFER");
if (!ValidateFramebufferTarget(target, "invalidateFramebuffer"))
return 0;
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("Bad target.");
}
if (!mBoundFramebuffer)
if (!fb)
return LOCAL_GL_FRAMEBUFFER_COMPLETE;
return mBoundFramebuffer->CheckFramebufferStatus().get();
return fb->CheckFramebufferStatus().get();
}
void
WebGLContext::CopyTexSubImage2D_base(TexImageTarget texImageTarget,
GLint level,
WebGLContext::CopyTexSubImage2D_base(TexImageTarget texImageTarget, GLint level,
TexInternalFormat internalformat,
GLint xoffset,
GLint yoffset,
GLint x,
GLint y,
GLsizei width,
GLsizei height,
GLint xoffset, GLint yoffset, GLint x,
GLint y, GLsizei width, GLsizei height,
bool sub)
{
const WebGLRectangleObject* framebufferRect = CurValidFBRectObject();
const WebGLRectangleObject* framebufferRect = CurValidReadFBRectObject();
GLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0;
GLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0;
@ -399,7 +433,7 @@ WebGLContext::CopyTexSubImage2D_base(TexImageTarget texImageTarget,
if (!ValidateCopyTexImage(internalformat.get(), func, dims))
return;
if (!mBoundFramebuffer)
if (!mBoundReadFramebuffer)
ClearBackbufferIfNeeded();
MakeContextCurrent();
@ -416,8 +450,8 @@ WebGLContext::CopyTexSubImage2D_base(TexImageTarget texImageTarget,
}
TexType framebuffertype = LOCAL_GL_NONE;
if (mBoundFramebuffer) {
TexInternalFormat framebuffereffectiveformat = mBoundFramebuffer->ColorAttachment(0).EffectiveInternalFormat();
if (mBoundReadFramebuffer) {
TexInternalFormat framebuffereffectiveformat = mBoundReadFramebuffer->ColorAttachment(0).EffectiveInternalFormat();
framebuffertype = TypeFromInternalFormat(framebuffereffectiveformat);
} else {
// FIXME - here we're assuming that the default framebuffer is backed by UNSIGNED_BYTE
@ -541,7 +575,7 @@ WebGLContext::CopyTexImage2D(GLenum rawTexImgTarget,
if (!ValidateCopyTexImage(internalformat, func, dims))
return;
if (!mBoundFramebuffer)
if (!mBoundReadFramebuffer)
ClearBackbufferIfNeeded();
CopyTexSubImage2D_base(rawTexImgTarget, level, internalformat, 0, 0, x, y, width, height, false);
@ -605,7 +639,7 @@ WebGLContext::CopyTexSubImage2D(GLenum rawTexImgTarget,
if (yoffset + height > texHeight || yoffset + height < 0)
return ErrorInvalidValue("copyTexSubImage2D: yoffset+height is too large");
if (!mBoundFramebuffer)
if (!mBoundReadFramebuffer)
ClearBackbufferIfNeeded();
if (imageInfo.HasUninitializedImageData()) {
@ -681,9 +715,18 @@ WebGLContext::DeleteFramebuffer(WebGLFramebuffer* fbuf)
fbuf->RequestDelete();
if (mBoundFramebuffer == fbuf)
BindFramebuffer(LOCAL_GL_FRAMEBUFFER,
if (mBoundReadFramebuffer == mBoundDrawFramebuffer) {
if (mBoundDrawFramebuffer == fbuf) {
BindFramebuffer(LOCAL_GL_FRAMEBUFFER,
static_cast<WebGLFramebuffer*>(nullptr));
}
} else if (mBoundDrawFramebuffer == fbuf) {
BindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
static_cast<WebGLFramebuffer*>(nullptr));
} else if (mBoundReadFramebuffer == fbuf) {
BindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER,
static_cast<WebGLFramebuffer*>(nullptr));
}
}
void
@ -698,8 +741,11 @@ WebGLContext::DeleteRenderbuffer(WebGLRenderbuffer* rbuf)
if (!rbuf || rbuf->IsDeleted())
return;
if (mBoundFramebuffer)
mBoundFramebuffer->DetachRenderbuffer(rbuf);
if (mBoundDrawFramebuffer)
mBoundDrawFramebuffer->DetachRenderbuffer(rbuf);
if (mBoundReadFramebuffer)
mBoundReadFramebuffer->DetachRenderbuffer(rbuf);
// Invalidate framebuffer status cache
rbuf->NotifyFBsStatusChanged();
@ -723,8 +769,11 @@ WebGLContext::DeleteTexture(WebGLTexture* tex)
if (!tex || tex->IsDeleted())
return;
if (mBoundFramebuffer)
mBoundFramebuffer->DetachTexture(tex);
if (mBoundDrawFramebuffer)
mBoundDrawFramebuffer->DetachTexture(tex);
if (mBoundReadFramebuffer)
mBoundReadFramebuffer->DetachTexture(tex);
// Invalidate framebuffer status cache
tex->NotifyFBsStatusChanged();
@ -823,19 +872,41 @@ WebGLContext::FramebufferRenderbuffer(GLenum target, GLenum attachment,
if (IsContextLost())
return;
if (!mBoundFramebuffer)
return ErrorInvalidOperation("framebufferRenderbuffer: cannot modify framebuffer 0");
if (target != LOCAL_GL_FRAMEBUFFER)
return ErrorInvalidEnumInfo("framebufferRenderbuffer: target", target);
if (rbtarget != LOCAL_GL_RENDERBUFFER)
return ErrorInvalidEnumInfo("framebufferRenderbuffer: renderbuffer target:", rbtarget);
if (!ValidateFramebufferAttachment(attachment, "framebufferRenderbuffer"))
if (!ValidateFramebufferTarget(target, "framebufferRenderbuffer"))
return;
return mBoundFramebuffer->FramebufferRenderbuffer(attachment, rbtarget, wrb);
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("Bad target.");
}
if (!fb) {
return ErrorInvalidOperation("framebufferRenderbuffer: cannot modify"
" framebuffer 0.");
}
if (rbtarget != LOCAL_GL_RENDERBUFFER) {
return ErrorInvalidEnumInfo("framebufferRenderbuffer: rbtarget:",
rbtarget);
}
if (!ValidateFramebufferAttachment(fb, attachment,
"framebufferRenderbuffer"))
{
return;
}
fb->FramebufferRenderbuffer(attachment, rbtarget, wrb);
}
void
@ -848,23 +919,41 @@ WebGLContext::FramebufferTexture2D(GLenum target,
if (IsContextLost())
return;
if (!mBoundFramebuffer)
return ErrorInvalidOperation("framebufferRenderbuffer: cannot modify framebuffer 0");
if (!ValidateFramebufferTarget(target, "framebufferTexture2D"))
return;
if (target != LOCAL_GL_FRAMEBUFFER)
return ErrorInvalidEnumInfo("framebufferTexture2D: target", target);
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("Bad target.");
}
if (!fb) {
return ErrorInvalidOperation("framebufferTexture2D: cannot modify"
" framebuffer 0.");
}
if (textarget != LOCAL_GL_TEXTURE_2D &&
(textarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
textarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))
{
return ErrorInvalidEnumInfo("framebufferTexture2D: invalid texture target", textarget);
return ErrorInvalidEnumInfo("framebufferTexture2D: textarget:",
textarget);
}
if (!ValidateFramebufferAttachment(attachment, "framebufferTexture2D"))
if (!ValidateFramebufferAttachment(fb, attachment, "framebufferTexture2D"))
return;
return mBoundFramebuffer->FramebufferTexture2D(attachment, textarget, tobj, level);
fb->FramebufferTexture2D(attachment, textarget, tobj, level);
}
void
@ -1148,25 +1237,42 @@ WebGLContext::GetFramebufferAttachmentParameter(JSContext* cx,
if (IsContextLost())
return JS::NullValue();
if (target != LOCAL_GL_FRAMEBUFFER) {
ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: target", target);
if (!ValidateFramebufferTarget(target, "getFramebufferAttachmentParameter"))
return JS::NullValue();
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("Bad target.");
}
if (!fb) {
ErrorInvalidOperation("getFramebufferAttachmentParameter: cannot query"
" framebuffer 0.");
return JS::NullValue();
}
if (!mBoundFramebuffer) {
ErrorInvalidOperation("getFramebufferAttachmentParameter: cannot query framebuffer 0");
if (!ValidateFramebufferAttachment(fb, attachment,
"getFramebufferAttachmentParameter"))
{
return JS::NullValue();
}
if (!ValidateFramebufferAttachment(attachment, "getFramebufferAttachmentParameter"))
return JS::NullValue();
if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers))
mBoundFramebuffer->EnsureColorAttachments(attachment - LOCAL_GL_COLOR_ATTACHMENT0);
fb->EnsureColorAttachments(attachment - LOCAL_GL_COLOR_ATTACHMENT0);
MakeContextCurrent();
const WebGLFramebuffer::Attachment& fba = mBoundFramebuffer->GetAttachment(attachment);
const WebGLFramebuffer::Attachment& fba = fb->GetAttachment(attachment);
if (fba.Renderbuffer()) {
switch (pname) {
@ -2256,7 +2362,7 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
if (pixels.IsNull())
return ErrorInvalidValue("readPixels: null destination buffer");
const WebGLRectangleObject* framebufferRect = CurValidFBRectObject();
const WebGLRectangleObject* framebufferRect = CurValidReadFBRectObject();
GLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0;
GLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0;
@ -2344,11 +2450,11 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
}
bool isSourceTypeFloat = false;
if (mBoundFramebuffer &&
mBoundFramebuffer->ColorAttachmentCount() &&
mBoundFramebuffer->ColorAttachment(0).IsDefined())
if (mBoundReadFramebuffer &&
mBoundReadFramebuffer->ColorAttachmentCount() &&
mBoundReadFramebuffer->ColorAttachment(0).IsDefined())
{
isSourceTypeFloat = mBoundFramebuffer->ColorAttachment(0).IsReadableFloat();
isSourceTypeFloat = mBoundReadFramebuffer->ColorAttachment(0).IsReadableFloat();
}
if (isReadTypeFloat != isSourceTypeFloat)
@ -2357,13 +2463,13 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
// Check the format and type params to assure they are an acceptable pair (as per spec)
MakeContextCurrent();
if (mBoundFramebuffer) {
if (mBoundReadFramebuffer) {
// prevent readback of arbitrary video memory through uninitialized renderbuffers!
if (!mBoundFramebuffer->CheckAndInitializeAttachments())
if (!mBoundReadFramebuffer->CheckAndInitializeAttachments())
return ErrorInvalidFramebufferOperation("readPixels: incomplete framebuffer");
GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT;
if (!mBoundFramebuffer->HasCompletePlanes(readPlaneBits)) {
if (!mBoundReadFramebuffer->HasCompletePlanes(readPlaneBits)) {
return ErrorInvalidOperation("readPixels: Read source attachment doesn't have the"
" correct color/depth/stencil type.");
}
@ -2486,8 +2592,8 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
return;
bool needAlphaFilled;
if (mBoundFramebuffer) {
needAlphaFilled = !mBoundFramebuffer->ColorAttachment(0).HasAlpha();
if (mBoundReadFramebuffer) {
needAlphaFilled = !mBoundReadFramebuffer->ColorAttachment(0).HasAlpha();
} else {
needAlphaFilled = !mOptions.alpha;
}

Просмотреть файл

@ -74,15 +74,15 @@ bool
WebGLContext::GetStencilBits(GLint* out_stencilBits)
{
*out_stencilBits = 0;
if (mBoundFramebuffer) {
if (mBoundFramebuffer->HasDepthStencilConflict()) {
if (mBoundDrawFramebuffer) {
if (mBoundDrawFramebuffer->HasDepthStencilConflict()) {
// Error, we don't know which stencil buffer's bits to use
ErrorInvalidFramebufferOperation("getParameter: framebuffer has two stencil buffers bound");
return false;
}
if (mBoundFramebuffer->StencilAttachment().IsDefined() ||
mBoundFramebuffer->DepthStencilAttachment().IsDefined())
if (mBoundDrawFramebuffer->StencilAttachment().IsDefined() ||
mBoundDrawFramebuffer->DepthStencilAttachment().IsDefined())
{
*out_stencilBits = 8;
}
@ -150,7 +150,7 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
} else if (pname >= LOCAL_GL_DRAW_BUFFER0 &&
pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + mGLMaxDrawBuffers))
{
if (mBoundFramebuffer) {
if (mBoundDrawFramebuffer) {
GLint iv = 0;
gl->fGetIntegerv(pname, &iv);
return JS::Int32Value(iv);
@ -190,6 +190,10 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
case LOCAL_GL_TEXTURE_BINDING_3D: {
return WebGLObjectAsJSValue(cx, mBound3DTextures[mActiveTexture].get(), rv);
}
// DRAW_FRAMEBUFFER_BINDING is the same as FRAMEBUFFER_BINDING.
case LOCAL_GL_READ_FRAMEBUFFER_BINDING:
return WebGLObjectAsJSValue(cx, mBoundReadFramebuffer.get(), rv);
}
}
@ -509,8 +513,9 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
return WebGLObjectAsJSValue(cx, mBoundRenderbuffer.get(), rv);
}
// DRAW_FRAMEBUFFER_BINDING is the same as FRAMEBUFFER_BINDING.
case LOCAL_GL_FRAMEBUFFER_BINDING: {
return WebGLObjectAsJSValue(cx, mBoundFramebuffer.get(), rv);
return WebGLObjectAsJSValue(cx, mBoundDrawFramebuffer.get(), rv);
}
case LOCAL_GL_CURRENT_PROGRAM: {

Просмотреть файл

@ -598,11 +598,14 @@ WebGLContext::EnumName(GLenum glenum)
XX(DEPTH_COMPONENT32);
XX(DEPTH_STENCIL);
XX(DEPTH24_STENCIL8);
XX(DRAW_FRAMEBUFFER);
XX(ETC1_RGB8_OES);
XX(FLOAT);
XX(FRAMEBUFFER);
XX(HALF_FLOAT);
XX(LUMINANCE);
XX(LUMINANCE_ALPHA);
XX(READ_FRAMEBUFFER);
XX(RGB);
XX(RGB16F);
XX(RGB32F);
@ -771,8 +774,6 @@ WebGLContext::EnumName(GLenum glenum)
XX(DEPTH_STENCIL_ATTACHMENT);
XX(UNSIGNED_NORMALIZED);
XX(DRAW_FRAMEBUFFER_BINDING);
XX(READ_FRAMEBUFFER);
XX(DRAW_FRAMEBUFFER);
XX(READ_FRAMEBUFFER_BINDING);
XX(RENDERBUFFER_SAMPLES);
XX(FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER);
@ -1028,10 +1029,21 @@ WebGLContext::AssertCachedBindings()
}
// Bound object state
GLuint bound = mBoundFramebuffer ? mBoundFramebuffer->GLName() : 0;
AssertUintParamCorrect(gl, LOCAL_GL_FRAMEBUFFER_BINDING, bound);
if (IsWebGL2()) {
GLuint bound = mBoundDrawFramebuffer ? mBoundDrawFramebuffer->GLName()
: 0;
AssertUintParamCorrect(gl, LOCAL_GL_DRAW_FRAMEBUFFER_BINDING, bound);
bound = mCurrentProgram ? mCurrentProgram->GLName() : 0;
bound = mBoundReadFramebuffer ? mBoundReadFramebuffer->GLName() : 0;
AssertUintParamCorrect(gl, LOCAL_GL_READ_FRAMEBUFFER_BINDING, bound);
} else {
MOZ_ASSERT(mBoundDrawFramebuffer == mBoundReadFramebuffer);
GLuint bound = mBoundDrawFramebuffer ? mBoundDrawFramebuffer->GLName()
: 0;
AssertUintParamCorrect(gl, LOCAL_GL_FRAMEBUFFER_BINDING, bound);
}
GLuint bound = mCurrentProgram ? mCurrentProgram->GLName() : 0;
AssertUintParamCorrect(gl, LOCAL_GL_CURRENT_PROGRAM, bound);
// Textures

Просмотреть файл

@ -416,10 +416,10 @@ bool WebGLContext::ValidateGLSLString(const nsAString& string, const char* info)
* be one of depth/stencil/depth_stencil/color attachment.
*/
bool
WebGLContext::ValidateFramebufferAttachment(GLenum attachment,
WebGLContext::ValidateFramebufferAttachment(const WebGLFramebuffer* fb, GLenum attachment,
const char* funcName)
{
if (!mBoundFramebuffer) {
if (!fb) {
switch (attachment) {
case LOCAL_GL_COLOR:
case LOCAL_GL_DEPTH:
@ -1328,27 +1328,26 @@ WebGLContext::ValidateCopyTexImage(GLenum format, WebGLTexImageFunc func,
// Default framebuffer format
GLenum fboFormat = mOptions.alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
if (mBoundFramebuffer) {
if (!mBoundFramebuffer->CheckAndInitializeAttachments()) {
if (mBoundReadFramebuffer) {
if (!mBoundReadFramebuffer->CheckAndInitializeAttachments()) {
ErrorInvalidFramebufferOperation("%s: Incomplete framebuffer.",
InfoFrom(func, dims));
return false;
}
GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT;
if (!mBoundFramebuffer->HasCompletePlanes(readPlaneBits)) {
if (!mBoundReadFramebuffer->HasCompletePlanes(readPlaneBits)) {
ErrorInvalidOperation("%s: Read source attachment doesn't have the"
" correct color/depth/stencil type.",
InfoFrom(func, dims));
return false;
}
// Get the correct format for the framebuffer, as it's not the default
// one.
// Get the correct format for the framebuffer, as it's not the default one.
const WebGLFramebuffer::Attachment& color0 =
mBoundFramebuffer->GetAttachment(LOCAL_GL_COLOR_ATTACHMENT0);
mBoundReadFramebuffer->GetAttachment(LOCAL_GL_COLOR_ATTACHMENT0);
fboFormat = mBoundFramebuffer->GetFormatForAttachment(color0);
fboFormat = mBoundReadFramebuffer->GetFormatForAttachment(color0);
}
// Make sure the format of the framebuffer is a superset of the format
@ -1949,7 +1948,8 @@ WebGLContext::InitAndValidateGL()
mBoundTransformFeedbackBuffer = nullptr;
mCurrentProgram = nullptr;
mBoundFramebuffer = nullptr;
mBoundDrawFramebuffer = nullptr;
mBoundReadFramebuffer = nullptr;
mBoundRenderbuffer = nullptr;
MakeContextCurrent();
@ -2143,4 +2143,32 @@ WebGLContext::InitAndValidateGL()
return true;
}
bool
WebGLContext::ValidateFramebufferTarget(GLenum target,
const char* const info)
{
bool isValid = true;
switch (target) {
case LOCAL_GL_FRAMEBUFFER:
break;
case LOCAL_GL_DRAW_FRAMEBUFFER:
case LOCAL_GL_READ_FRAMEBUFFER:
isValid = IsWebGL2();
break;
default:
isValid = false;
break;
}
if (MOZ_LIKELY(isValid)) {
return true;
}
ErrorInvalidEnum("%s: Invalid target: %s (0x%04x).", info, EnumName(target),
target);
return false;
}
} // namespace mozilla

Просмотреть файл

@ -455,7 +455,8 @@ WebGLFramebuffer::FramebufferRenderbuffer(FBAttachment attachPoint,
RBTarget rbtarget,
WebGLRenderbuffer* rb)
{
MOZ_ASSERT(mContext->mBoundFramebuffer == this);
MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
mContext->mBoundReadFramebuffer == this);
if (!mContext->ValidateObjectAllowNull("framebufferRenderbuffer: renderbuffer",
rb))
@ -496,7 +497,8 @@ WebGLFramebuffer::FramebufferTexture2D(FBAttachment attachPoint,
TexImageTarget texImageTarget,
WebGLTexture* tex, GLint level)
{
MOZ_ASSERT(mContext->mBoundFramebuffer == this);
MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
mContext->mBoundReadFramebuffer == this);
if (!mContext->ValidateObjectAllowNull("framebufferTexture2D: texture",
tex))
@ -564,7 +566,7 @@ WebGLFramebuffer::GetAttachmentOrNull(FBAttachment attachPoint)
break;
}
if (!mContext->ValidateFramebufferAttachment(attachPoint.get(),
if (!mContext->ValidateFramebufferAttachment(this, attachPoint.get(),
"getAttachmentOrNull"))
{
return nullptr;
@ -593,7 +595,7 @@ WebGLFramebuffer::GetAttachment(FBAttachment attachPoint) const
break;
}
if (!mContext->ValidateFramebufferAttachment(attachPoint.get(),
if (!mContext->ValidateFramebufferAttachment(this, attachPoint.get(),
"getAttachment"))
{
MOZ_ASSERT(false);
@ -766,7 +768,8 @@ WebGLFramebuffer::RectangleObject() const
FBStatus
WebGLFramebuffer::PrecheckFramebufferStatus() const
{
MOZ_ASSERT(mContext->mBoundFramebuffer == this);
MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
mContext->mBoundReadFramebuffer == this);
if (!HasDefinedAttachments())
return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; // No attachments
@ -809,7 +812,9 @@ WebGLFramebuffer::HasCompletePlanes(GLbitfield mask)
if (CheckFramebufferStatus() != LOCAL_GL_FRAMEBUFFER_COMPLETE)
return false;
MOZ_ASSERT(mContext->mBoundFramebuffer == this);
MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
mContext->mBoundReadFramebuffer == this);
bool hasPlanes = true;
if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
hasPlanes &= ColorAttachmentCount() &&
@ -832,7 +837,8 @@ WebGLFramebuffer::HasCompletePlanes(GLbitfield mask)
bool
WebGLFramebuffer::CheckAndInitializeAttachments()
{
MOZ_ASSERT(mContext->mBoundFramebuffer == this);
MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
mContext->mBoundReadFramebuffer == this);
if (CheckFramebufferStatus() != LOCAL_GL_FRAMEBUFFER_COMPLETE)
return false;

Просмотреть файл

@ -620,7 +620,11 @@ DrawBuffer::Create(GLContext* const gl,
gl->fGenFramebuffers(1, &fb);
gl->AttachBuffersToFB(0, colorMSRB, depthRB, stencilRB, fb);
UniquePtr<DrawBuffer> ret( new DrawBuffer(gl, size, fb, colorMSRB,
GLsizei samples = formats.samples;
if (!samples)
samples = 1;
UniquePtr<DrawBuffer> ret( new DrawBuffer(gl, size, samples, fb, colorMSRB,
depthRB, stencilRB) );
GLenum err = localError.GetError();

Просмотреть файл

@ -46,6 +46,7 @@ protected:
GLContext* const mGL;
public:
const gfx::IntSize mSize;
const GLsizei mSamples;
const GLuint mFB;
protected:
const GLuint mColorMSRB;
@ -54,12 +55,14 @@ protected:
DrawBuffer(GLContext* gl,
const gfx::IntSize& size,
GLsizei samples,
GLuint fb,
GLuint colorMSRB,
GLuint depthRB,
GLuint stencilRB)
: mGL(gl)
, mSize(size)
, mSamples(samples)
, mFB(fb)
, mColorMSRB(colorMSRB)
, mDepthRB(depthRB)
@ -198,6 +201,13 @@ public:
return mRead->mFB;
}
GLsizei Samples() const {
if (!mDraw)
return 1;
return mDraw->mSamples;
}
void DeletingFB(GLuint fb);
const gfx::IntSize& Size() const {