Exemplo n.º 1
0
static bool ClearDepthTexture(const WebGLContext& webgl, const GLuint tex,
                              const TexImageTarget imageTarget,
                              const uint32_t level,
                              const webgl::FormatUsageInfo* const usage,
                              const uint32_t depth) {
  // Depth resources actually clear to 1.0f, not 0.0f!
  // They are also always renderable.
  MOZ_ASSERT(usage->IsRenderable());

  const auto& gl = webgl.gl;
  const auto& format = usage->format;

  GLenum attachPoint = LOCAL_GL_DEPTH_ATTACHMENT;
  GLbitfield clearBits = LOCAL_GL_DEPTH_BUFFER_BIT;

  if (format->s) {
    attachPoint = LOCAL_GL_DEPTH_STENCIL_ATTACHMENT;
    clearBits |= LOCAL_GL_STENCIL_BUFFER_BIT;
  }

  // -

  gl::ScopedFramebuffer scopedFB(gl);
  const gl::ScopedBindFramebuffer scopedBindFB(gl, scopedFB.FB());
  const webgl::ScopedPrepForResourceClear scopedPrep(webgl);

  const auto fnAttach = [&](const uint32_t z) {
    switch (imageTarget.get()) {
      case LOCAL_GL_TEXTURE_3D:
      case LOCAL_GL_TEXTURE_2D_ARRAY:
        gl->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER, attachPoint, tex,
                                     level, z);
        break;
      default:
        if (attachPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
          gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
                                    LOCAL_GL_DEPTH_ATTACHMENT,
                                    imageTarget.get(), tex, level);
          gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
                                    LOCAL_GL_STENCIL_ATTACHMENT,
                                    imageTarget.get(), tex, level);
        } else {
          gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, attachPoint,
                                    imageTarget.get(), tex, level);
        }
        break;
    }
  };

  for (uint32_t z = 0; z < depth; ++z) {
    fnAttach(z);
    gl->fClear(clearBits);
  }
  const auto& status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
  const bool isComplete = (status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
  MOZ_ASSERT(isComplete);
  return isComplete;
}
Exemplo n.º 2
0
TexTarget TexImageTargetToTexTarget(TexImageTarget texImageTarget) {
  switch (texImageTarget.get()) {
    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
      return LOCAL_GL_TEXTURE_CUBE_MAP;

    default:
      return texImageTarget.get();
  }
}
Exemplo n.º 3
0
void
TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
                                WebGLTexture* tex, TexImageTarget target, GLint level,
                                const webgl::DriverUnpackInfo* dui, GLint xOffset,
                                GLint yOffset, GLint zOffset, GLenum* const out_glError)
{
    *out_glError = 0;

    WebGLContext* webgl = tex->mContext;

    // MakeCurrent is a big mess in here, because mapping (and presumably unmapping) on
    // OSX can lose our MakeCurrent. Therefore it's easiest to MakeCurrent just before we
    // call into GL, instead of trying to keep MakeCurrent-ed.

    RefPtr<gfx::DataSourceSurface> dataSurf = mSurf->GetDataSurface();

    if (!dataSurf) {
        // Since GetDataSurface didn't return error code, assume system
        // is out of memory
        *out_glError = LOCAL_GL_OUT_OF_MEMORY;
        return;
    }

    GLenum error;
    if (UploadDataSurface(isSubImage, webgl, target, level, dui, xOffset, yOffset,
                          zOffset, mWidth, mHeight, dataSurf, mIsAlphaPremult, &error))
    {
        return;
    }
    if (error == LOCAL_GL_OUT_OF_MEMORY) {
        *out_glError = LOCAL_GL_OUT_OF_MEMORY;
        return;
    }

    // CPU conversion. (++numCopies)

    UniqueBuffer convertedBuffer;
    uint8_t convertedAlignment;
    bool outOfMemory;
    if (!ConvertSurface(webgl, dui, dataSurf, mIsAlphaPremult, &convertedBuffer,
                        &convertedAlignment, &outOfMemory))
    {
        if (outOfMemory) {
            *out_glError = LOCAL_GL_OUT_OF_MEMORY;
        } else {
            NS_ERROR("Failed to convert surface.");
            *out_glError = LOCAL_GL_OUT_OF_MEMORY;
        }
        return;
    }

    MOZ_ALWAYS_TRUE( webgl->gl->MakeCurrent() );
    ScopedUnpackReset scopedReset(webgl);
    webgl->gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, convertedAlignment);

    error = DoTexOrSubImage(isSubImage, webgl->gl, target.get(), level, dui, xOffset,
                            yOffset, zOffset, mWidth, mHeight, mDepth,
                            convertedBuffer.get());
    *out_glError = error;
}
Exemplo n.º 4
0
WebGLContext::FakeBlackTexture::FakeBlackTexture(gl::GLContext* gl, TexTarget target,
                                                 FakeBlackType type)
    : mGL(gl)
    , mGLName(CreateGLTexture(gl))
{
    GLenum texFormat;
    switch (type) {
    case FakeBlackType::RGBA0000:
        texFormat = LOCAL_GL_RGBA;
        break;

    case FakeBlackType::RGBA0001:
        texFormat = LOCAL_GL_RGB;
        break;

    default:
        MOZ_CRASH("bad type");
    }

    gl::ScopedBindTexture scopedBind(mGL, mGLName, target.get());

    mGL->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST);
    mGL->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);

    // We allocate our zeros on the heap, and we overallocate (16 bytes instead of 4) to
    // minimize the risk of running into a driver bug in texImage2D, as it is a bit
    // unusual maybe to create 1x1 textures, and the stack may not have the alignment that
    // TexImage2D expects.

    const webgl::DriverUnpackInfo dui = {texFormat, texFormat, LOCAL_GL_UNSIGNED_BYTE};
    UniqueBuffer zeros = moz_xcalloc(1, 16); // Infallible allocation.

    if (target == LOCAL_GL_TEXTURE_CUBE_MAP) {
        for (int i = 0; i < 6; ++i) {
            const TexImageTarget curTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;
            const GLenum error = DoTexImage(mGL, curTarget.get(), 0, &dui, 1, 1, 1,
                                            zeros.get());
            if (error)
                MOZ_CRASH("Unexpected error during FakeBlack creation.");
        }
    } else {
        const GLenum error = DoTexImage(mGL, target.get(), 0, &dui, 1, 1, 1,
                                        zeros.get());
        if (error)
            MOZ_CRASH("Unexpected error during FakeBlack creation.");
    }
}
Exemplo n.º 5
0
TexTarget
TexImageTargetToTexTarget(TexImageTarget texImageTarget)
{
    switch (texImageTarget.get()) {
    case LOCAL_GL_TEXTURE_2D:
    case LOCAL_GL_TEXTURE_3D:
        return texImageTarget.get();
    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
        return LOCAL_GL_TEXTURE_CUBE_MAP;
    default:
        MOZ_ASSERT(false, "Bad texture target");
        // Should be caught by the constructor for TexTarget
        return LOCAL_GL_NONE;
    }
}
Exemplo n.º 6
0
// `mask` from glClear.
static bool
ClearWithTempFB(WebGLContext* webgl, GLuint tex,
                TexImageTarget texImageTarget, GLint level,
                TexInternalFormat baseInternalFormat,
                GLsizei width, GLsizei height)
{
    MOZ_ASSERT(texImageTarget == LOCAL_GL_TEXTURE_2D);

    gl::GLContext* gl = webgl->GL();
    MOZ_ASSERT(gl->IsCurrent());

    gl::ScopedFramebuffer fb(gl);
    gl::ScopedBindFramebuffer autoFB(gl, fb.FB());
    GLbitfield mask = 0;

    switch (baseInternalFormat.get()) {
    case LOCAL_GL_LUMINANCE:
    case LOCAL_GL_LUMINANCE_ALPHA:
    case LOCAL_GL_ALPHA:
    case LOCAL_GL_RGB:
    case LOCAL_GL_RGBA:
    case LOCAL_GL_BGR:
    case LOCAL_GL_BGRA:
        mask = LOCAL_GL_COLOR_BUFFER_BIT;
        gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
                                  texImageTarget.get(), tex, level);
        break;
    case LOCAL_GL_DEPTH_COMPONENT32_OES:
    case LOCAL_GL_DEPTH_COMPONENT24_OES:
    case LOCAL_GL_DEPTH_COMPONENT16:
    case LOCAL_GL_DEPTH_COMPONENT:
        mask = LOCAL_GL_DEPTH_BUFFER_BIT;
        gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT,
                                  texImageTarget.get(), tex, level);
        break;

    case LOCAL_GL_DEPTH24_STENCIL8:
    case LOCAL_GL_DEPTH_STENCIL:
        mask = LOCAL_GL_DEPTH_BUFFER_BIT |
               LOCAL_GL_STENCIL_BUFFER_BIT;
        gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT,
                                  texImageTarget.get(), tex, level);
        gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT,
                                  texImageTarget.get(), tex, level);
        break;

    default:
        return false;
    }
    MOZ_ASSERT(mask);

    if (ClearByMask(webgl, mask))
        return true;

    // Failed to simply build an FB from the tex, but maybe it needs a
    // color buffer to be complete.

    if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
        // Nope, it already had one.
        return false;
    }

    gl::ScopedRenderbuffer rb(gl);
    {
        // Only GLES guarantees RGBA4.
        GLenum format = gl->IsGLES() ? LOCAL_GL_RGBA4 : LOCAL_GL_RGBA8;
        gl::ScopedBindRenderbuffer rbBinding(gl, rb.RB());
        gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, format, width, height);
    }

    gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
                                 LOCAL_GL_RENDERBUFFER, rb.RB());
    mask |= LOCAL_GL_COLOR_BUFFER_BIT;

    // Last chance!
    return ClearByMask(webgl, mask);
}
Exemplo n.º 7
0
bool
TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
                              WebGLTexture* tex, TexImageTarget target, GLint level,
                              const webgl::DriverUnpackInfo* dui, GLint xOffset,
                              GLint yOffset, GLint zOffset, const webgl::PackingInfo& pi,
                              GLenum* const out_error) const
{
    MOZ_ASSERT_IF(needsRespec, !isSubImage);

    WebGLContext* webgl = tex->mContext;

    gl::GLContext* gl = webgl->GL();
    gl->MakeCurrent();

    if (needsRespec) {
        *out_error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dui, xOffset,
                                     yOffset, zOffset, mWidth, mHeight, mDepth,
                                     nullptr);
        if (*out_error)
            return true;
    }

    const char* fallbackReason;
    do {
        if (mDepth != 1) {
            fallbackReason = "depth is not 1";
            break;
        }

        if (webgl->mPixelStore_UnpackSkipPixels ||
            webgl->mPixelStore_UnpackSkipRows ||
            webgl->mPixelStore_UnpackSkipImages)
        {
            fallbackReason = "non-zero UNPACK_SKIP_* not yet supported";
            break;
        }

        const auto fnHasPremultMismatch = [&]() {
            if (mSrcAlphaType == gfxAlphaType::Opaque)
                return false;

            const bool srcIsPremult = (mSrcAlphaType == gfxAlphaType::Premult);
            const auto& dstIsPremult = webgl->mPixelStore_PremultiplyAlpha;
            if (srcIsPremult == dstIsPremult)
                return false;

            if (dstIsPremult) {
                fallbackReason = "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not true";
            } else {
                fallbackReason = "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not false";
            }
            return true;
        };
        if (fnHasPremultMismatch())
            break;

        if (dui->unpackFormat != LOCAL_GL_RGB && dui->unpackFormat != LOCAL_GL_RGBA) {
            fallbackReason = "`format` is not RGB or RGBA";
            break;
        }

        if (dui->unpackType != LOCAL_GL_UNSIGNED_BYTE) {
            fallbackReason = "`type` is not UNSIGNED_BYTE";
            break;
        }

        gl::ScopedFramebuffer scopedFB(gl);
        gl::ScopedBindFramebuffer bindFB(gl, scopedFB.FB());

        {
            gl::GLContext::LocalErrorScope errorScope(*gl);

            gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
                                      target.get(), tex->mGLName, level);

            if (errorScope.GetError()) {
                fallbackReason = "bug: failed to attach to FB for blit";
                break;
            }
        }

        const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
        if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
            fallbackReason = "bug: failed to confirm FB for blit";
            break;
        }

        const gfx::IntSize destSize(mWidth, mHeight);
        const auto dstOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft
                                                         : gl::OriginPos::BottomLeft);
        if (!gl->BlitHelper()->BlitImageToFramebuffer(mImage, destSize, scopedFB.FB(),
                                                      dstOrigin))
        {
            fallbackReason = "likely bug: failed to blit";
            break;
        }

        // Blitting was successful, so we're done!
        *out_error = 0;
        return true;
    } while (false);

    const nsPrintfCString perfMsg("%s: Failed to hit GPU-copy fast-path: %s (src type %u)",
                                  funcName, fallbackReason, uint32_t(mImage->GetFormat()));

    if (webgl->mPixelStore_RequireFastPath) {
        webgl->ErrorInvalidOperation("%s", perfMsg.BeginReading());
        return false;
    }

    webgl->GeneratePerfWarning("%s Falling back to CPU upload.",
                               perfMsg.BeginReading());

    const RefPtr<gfx::SourceSurface> surf = mImage->GetAsSourceSurface();

    RefPtr<gfx::DataSourceSurface> dataSurf;
    if (surf) {
        // WARNING: OSX can lose our MakeCurrent here.
        dataSurf = surf->GetDataSurface();
    }
    if (!dataSurf) {
        webgl->ErrorOutOfMemory("%s: GetAsSourceSurface or GetDataSurface failed after"
                                " blit failed for TexUnpackImage.",
                                funcName);
        return false;
    }

    const TexUnpackSurface surfBlob(webgl, target, mWidth, mHeight, mDepth, dataSurf,
                                    mSrcAlphaType);

    return surfBlob.TexOrSubImage(isSubImage, needsRespec, funcName, tex, target, level,
                                  dui, xOffset, yOffset, zOffset, pi, out_error);
}
Exemplo n.º 8
0
bool
TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
                                WebGLTexture* tex, TexImageTarget target, GLint level,
                                const webgl::DriverUnpackInfo* dui, GLint xOffset,
                                GLint yOffset, GLint zOffset, const webgl::PackingInfo& dstPI,
                                GLenum* const out_error) const
{
    const auto& webgl = tex->mContext;

    ////

    const auto rowLength = mSurf->GetSize().width;
    const auto rowCount = mSurf->GetSize().height;

    const auto& dstBPP = webgl::BytesPerPixel(dstPI);
    const auto dstFormat = FormatForPackingInfo(dstPI);

    ////

    WebGLTexelFormat srcFormat;
    uint8_t srcBPP;
    if (!GetFormatForSurf(mSurf, &srcFormat, &srcBPP)) {
        webgl->ErrorImplementationBug("%s: GetFormatForSurf failed for"
                                      " WebGLTexelFormat::%u.",
                                      funcName, uint32_t(mSurf->GetFormat()));
        return false;
    }

    gfx::DataSourceSurface::ScopedMap map(mSurf, gfx::DataSourceSurface::MapType::READ);
    if (!map.IsMapped()) {
        webgl->ErrorOutOfMemory("%s: Failed to map source surface for upload.", funcName);
        return false;
    }

    const auto& srcBegin = map.GetData();
    const auto& srcStride = map.GetStride();

    ////

    const auto srcRowLengthBytes = rowLength * srcBPP;

    const uint8_t maxGLAlignment = 8;
    uint8_t srcAlignment = 1;
    for (; srcAlignment <= maxGLAlignment; srcAlignment *= 2) {
        const auto strideGuess = RoundUpToMultipleOf(srcRowLengthBytes, srcAlignment);
        if (strideGuess == srcStride)
            break;
    }
    const uint32_t dstAlignment = (srcAlignment > maxGLAlignment) ? 1 : srcAlignment;

    const auto dstRowLengthBytes = rowLength * dstBPP;
    const auto dstStride = RoundUpToMultipleOf(dstRowLengthBytes, dstAlignment);

    ////

    const uint8_t* dstBegin = srcBegin;
    UniqueBuffer tempBuffer;
    if (!ConvertIfNeeded(webgl, funcName, rowLength, rowCount, srcFormat, srcBegin,
                         srcStride, dstFormat, dstStride, &dstBegin, &tempBuffer))
    {
        return false;
    }

    ////

    const auto& gl = webgl->gl;
    if (!gl->MakeCurrent()) {
        *out_error = LOCAL_GL_CONTEXT_LOST;
        return true;
    }

    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, dstAlignment);
    if (webgl->IsWebGL2()) {
        gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength);
    }

    *out_error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dui, xOffset,
                                 yOffset, zOffset, mWidth, mHeight, mDepth, dstBegin);

    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, webgl->mPixelStore_UnpackAlignment);
    if (webgl->IsWebGL2()) {
        gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, webgl->mPixelStore_UnpackRowLength);
    }

    return true;
}
Exemplo n.º 9
0
bool
TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
                                WebGLTexture* tex, TexImageTarget target, GLint level,
                                const webgl::DriverUnpackInfo* dstDUI, GLint xOffset,
                                GLint yOffset, GLint zOffset,
                                GLenum* const out_error) const
{
    WebGLContext* webgl = tex->mContext;

    WebGLTexelFormat srcFormat;
    uint8_t srcBPP;
    if (!GetFormatForSurf(mSurf, &srcFormat, &srcBPP)) {
        webgl->ErrorImplementationBug("%s: GetFormatForSurf failed for"
                                      " WebGLTexelFormat::%u.",
                                      funcName, uint32_t(mSurf->GetFormat()));
        return false;
    }

    gfx::DataSourceSurface::ScopedMap map(mSurf, gfx::DataSourceSurface::MapType::READ);
    if (!map.IsMapped())
        return false;

    const auto srcBytes = map.GetData();
    const auto srcStride = map.GetStride();

    // CPU conversion. (++numCopies)

    webgl->GenerateWarning("%s: Incurred CPU-side conversion, which is very slow.",
                           funcName);

    const uint8_t* uploadBytes;
    UniqueBuffer tempBuffer;
    if (!ConvertIfNeeded(webgl, funcName, srcBytes, srcStride, srcBPP, srcFormat,
                         dstDUI, &uploadBytes, &tempBuffer))
    {
        return false;
    }

    //////

    gl::GLContext* const gl = webgl->gl;
    MOZ_ALWAYS_TRUE( gl->MakeCurrent() );

    const auto curEffectiveRowLength = FallbackOnZero(webgl->mPixelStore_UnpackRowLength,
                                                      mWidth);

    const bool changeRowLength = (mRowLength != curEffectiveRowLength);
    if (changeRowLength) {
        MOZ_ASSERT(webgl->IsWebGL2());
        gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, mRowLength);
    }

    *out_error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dstDUI, xOffset,
                                 yOffset, zOffset, mWidth, mHeight, mDepth, uploadBytes);

    if (changeRowLength) {
        gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, webgl->mPixelStore_UnpackRowLength);
    }

    return true;
}
Exemplo n.º 10
0
static bool
ZeroTextureData(WebGLContext* webgl, const char* funcName, GLuint tex,
                TexImageTarget target, uint32_t level,
                const webgl::FormatUsageInfo* usage, uint32_t width, uint32_t height,
                uint32_t depth)
{
    // This has two usecases:
    // 1. Lazy zeroing of uninitialized textures:
    //    a. Before draw, when FakeBlack isn't viable. (TexStorage + Draw*)
    //    b. Before partial upload. (TexStorage + TexSubImage)
    // 2. Zero subrects from out-of-bounds blits. (CopyTex(Sub)Image)

    // We have no sympathy for any of these cases.

    // "Doctor, it hurts when I do this!" "Well don't do that!"
    webgl->GenerateWarning("%s: This operation requires zeroing texture data. This is"
                           " slow.",
                           funcName);

    gl::GLContext* gl = webgl->GL();
    gl->MakeCurrent();

    GLenum scopeBindTarget;
    switch (target.get()) {
    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
        scopeBindTarget = LOCAL_GL_TEXTURE_CUBE_MAP;
        break;
    default:
        scopeBindTarget = target.get();
        break;
    }
    const gl::ScopedBindTexture scopeBindTexture(gl, tex, scopeBindTarget);
    auto compression = usage->format->compression;
    if (compression) {
        auto sizedFormat = usage->format->sizedFormat;
        MOZ_RELEASE_ASSERT(sizedFormat, "GFX: texture sized format not set");

        const auto fnSizeInBlocks = [](CheckedUint32 pixels, uint8_t pixelsPerBlock) {
            return RoundUpToMultipleOf(pixels, pixelsPerBlock) / pixelsPerBlock;
        };

        const auto widthBlocks = fnSizeInBlocks(width, compression->blockWidth);
        const auto heightBlocks = fnSizeInBlocks(height, compression->blockHeight);

        CheckedUint32 checkedByteCount = compression->bytesPerBlock;
        checkedByteCount *= widthBlocks;
        checkedByteCount *= heightBlocks;
        checkedByteCount *= depth;

        if (!checkedByteCount.isValid())
            return false;

        const size_t byteCount = checkedByteCount.value();

        UniqueBuffer zeros = calloc(1, byteCount);
        if (!zeros)
            return false;

        ScopedUnpackReset scopedReset(webgl);
        gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it
                                                        // well.

        const auto error = DoCompressedTexSubImage(gl, target.get(), level, 0, 0, 0,
                                                   width, height, depth, sizedFormat,
                                                   byteCount, zeros.get());
        return !error;
    }

    const auto driverUnpackInfo = usage->idealUnpack;
    MOZ_RELEASE_ASSERT(driverUnpackInfo, "GFX: ideal unpack info not set.");

    if (webgl->IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture) &&
        gl->IsANGLE() &&
        usage->format->d)
    {
        // ANGLE_depth_texture does not allow uploads, so we have to clear.
        // (Restriction because of D3D9)
        MOZ_ASSERT(target == LOCAL_GL_TEXTURE_2D);
        MOZ_ASSERT(level == 0);
        ZeroANGLEDepthTexture(webgl, tex, usage, width, height);
        return true;
    }

    const webgl::PackingInfo packing = driverUnpackInfo->ToPacking();

    const auto bytesPerPixel = webgl::BytesPerPixel(packing);

    CheckedUint32 checkedByteCount = bytesPerPixel;
    checkedByteCount *= width;
    checkedByteCount *= height;
    checkedByteCount *= depth;

    if (!checkedByteCount.isValid())
        return false;

    const size_t byteCount = checkedByteCount.value();

    UniqueBuffer zeros = calloc(1, byteCount);
    if (!zeros)
        return false;

    ScopedUnpackReset scopedReset(webgl);
    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it well.
    const auto error = DoTexSubImage(gl, target, level, 0, 0, 0, width, height, depth,
                                     packing, zeros.get());
    return !error;
}
Exemplo n.º 11
0
void
WebGL2Context::TexImage3D(GLenum target, GLint level, GLenum internalformat,
                          GLsizei width, GLsizei height, GLsizei depth,
                          GLint border, GLenum format, GLenum type,
                          const Nullable<dom::ArrayBufferView> &pixels,
                          ErrorResult& rv)
{
    if (IsContextLost())
        return;

    void* data;
    size_t dataLength;
    js::Scalar::Type jsArrayType;
    if (pixels.IsNull()) {
        data = nullptr;
        dataLength = 0;
        jsArrayType = js::Scalar::TypeMax;
    } else {
        const ArrayBufferView& view = pixels.Value();
        view.ComputeLengthAndData();

        data = view.Data();
        dataLength = view.Length();
        jsArrayType = JS_GetArrayBufferViewType(view.Obj());
    }

    const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage;
    const WebGLTexDimensions dims = WebGLTexDimensions::Tex3D;

    if (!ValidateTexImageTarget(target, func, dims))
        return;

    TexImageTarget texImageTarget = target;

    if (!ValidateTexImage(texImageTarget, level, internalformat,
                          0, 0, 0,
                          width, height, depth,
                          border, format, type, func, dims))
    {
        return;
    }

    if (!ValidateTexInputData(type, jsArrayType, func, dims))
        return;

    TexInternalFormat effectiveInternalFormat =
        EffectiveInternalFormatFromInternalFormatAndType(internalformat, type);

    if (effectiveInternalFormat == LOCAL_GL_NONE) {
        return ErrorInvalidOperation("texImage3D: bad combination of internalformat and type");
    }

    // we need to find the exact sized format of the source data. Slightly abusing
    // EffectiveInternalFormatFromInternalFormatAndType for that purpose. Really, an unsized source format
    // is the same thing as an unsized internalformat.
    TexInternalFormat effectiveSourceFormat =
        EffectiveInternalFormatFromInternalFormatAndType(format, type);
    MOZ_ASSERT(effectiveSourceFormat != LOCAL_GL_NONE); // should have validated format/type combo earlier
    const size_t srcbitsPerTexel = GetBitsPerTexel(effectiveSourceFormat);
    MOZ_ASSERT((srcbitsPerTexel % 8) == 0); // should not have compressed formats here.
    size_t srcTexelSize = srcbitsPerTexel / 8;

    CheckedUint32 checked_neededByteLength =
        GetImageSize(height, width, depth, srcTexelSize, mPixelStoreUnpackAlignment);

    if (!checked_neededByteLength.isValid())
        return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");

    uint32_t bytesNeeded = checked_neededByteLength.value();

    if (dataLength && dataLength < bytesNeeded)
        return ErrorInvalidOperation("texImage3D: not enough data for operation (need %d, have %d)",
                                 bytesNeeded, dataLength);

    WebGLTexture* tex = activeBoundTextureForTexImageTarget(texImageTarget);

    if (!tex)
        return ErrorInvalidOperation("texImage3D: no texture is bound to this target");

    if (tex->IsImmutable()) {
        return ErrorInvalidOperation(
            "texImage3D: disallowed because the texture "
            "bound to this target has already been made immutable by texStorage3D");
    }

    GLenum driverType = LOCAL_GL_NONE;
    GLenum driverInternalFormat = LOCAL_GL_NONE;
    GLenum driverFormat = LOCAL_GL_NONE;
    DriverFormatsFromEffectiveInternalFormat(gl,
                                             effectiveInternalFormat,
                                             &driverInternalFormat,
                                             &driverFormat,
                                             &driverType);

    MakeContextCurrent();
    GetAndFlushUnderlyingGLErrors();
    gl->fTexImage3D(texImageTarget.get(), level,
                    driverInternalFormat,
                    width, height, depth,
                    0, driverFormat, driverType,
                    data);
    GLenum error = GetAndFlushUnderlyingGLErrors();
    if (error) {
        return GenerateWarning("texImage3D generated error %s", ErrorName(error));
    }

    tex->SetImageInfo(texImageTarget, level,
                      width, height, depth,
                      effectiveInternalFormat,
                      data ? WebGLImageDataStatus::InitializedImageData
                           : WebGLImageDataStatus::UninitializedImageData);
}
Exemplo n.º 12
0
bool
TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
                              WebGLTexture* tex, TexImageTarget target, GLint level,
                              const webgl::DriverUnpackInfo* dui, GLint xOffset,
                              GLint yOffset, GLint zOffset, GLenum* const out_error) const
{
    MOZ_ASSERT_IF(needsRespec, !isSubImage);

    WebGLContext* webgl = tex->mContext;

    gl::GLContext* gl = webgl->GL();
    gl->MakeCurrent();

    if (needsRespec) {
        *out_error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dui, xOffset,
                                     yOffset, zOffset, mWidth, mHeight, mDepth,
                                     nullptr);
        if (*out_error)
            return false;
    }

    do {
        if (mDepth != 1)
            break;

        const bool isDstPremult = webgl->mPixelStore_PremultiplyAlpha;
        if (mIsSrcPremult != isDstPremult)
            break;

        if (dui->unpackFormat != LOCAL_GL_RGB && dui->unpackFormat != LOCAL_GL_RGBA)
            break;

        if (dui->unpackType != LOCAL_GL_UNSIGNED_BYTE)
            break;

        gl::ScopedFramebuffer scopedFB(gl);
        gl::ScopedBindFramebuffer bindFB(gl, scopedFB.FB());

        {
            gl::GLContext::LocalErrorScope errorScope(*gl);

            gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
                                      target.get(), tex->mGLName, level);

            if (errorScope.GetError())
                break;
        }

        const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
        if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
            break;

        const gfx::IntSize destSize(mWidth, mHeight);
        const auto dstOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft
                                                         : gl::OriginPos::BottomLeft);
        if (!gl->BlitHelper()->BlitImageToFramebuffer(mImage, destSize, scopedFB.FB(),
                                                      dstOrigin))
        {
            break;
        }

        // Blitting was successful, so we're done!
        *out_error = 0;
        return true;
    } while (false);

    webgl->GenerateWarning("%s: Failed to hit GPU-copy fast-path. Falling back to CPU"
                           " upload.",
                           funcName);

    const RefPtr<gfx::SourceSurface> surf = mImage->GetAsSourceSurface();

    RefPtr<gfx::DataSourceSurface> dataSurf;
    if (surf) {
        // WARNING: OSX can lose our MakeCurrent here.
        dataSurf = surf->GetDataSurface();
    }
    if (!dataSurf) {
        webgl->ErrorOutOfMemory("%s: GetAsSourceSurface or GetDataSurface failed after"
                                " blit failed for TexUnpackImage.",
                                funcName);
        return false;
    }

    const TexUnpackSurface surfBlob(webgl, target, mWidth, mHeight, mDepth, dataSurf,
                                    mIsSrcPremult);

    return surfBlob.TexOrSubImage(isSubImage, needsRespec, funcName, tex, target, level,
                                  dui, xOffset, yOffset, zOffset, out_error);
}
Exemplo n.º 13
0
bool
WebGLTexture::EnsureInitializedImageData(TexImageTarget imageTarget,
                                         GLint level)
{
    const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level);
    if (!imageInfo.HasUninitializedImageData())
        return true;

    mContext->MakeContextCurrent();

    // Try to clear with glClear.
    if (imageTarget == LOCAL_GL_TEXTURE_2D) {
        bool cleared = ClearWithTempFB(mContext, mGLName, imageTarget, level,
                                       imageInfo.mEffectiveInternalFormat,
                                       imageInfo.mHeight, imageInfo.mWidth);
        if (cleared) {
            SetImageDataStatus(imageTarget, level,
                               WebGLImageDataStatus::InitializedImageData);
            return true;
        }
    }

    // That didn't work. Try uploading zeros then.
    size_t bitspertexel = GetBitsPerTexel(imageInfo.mEffectiveInternalFormat);
    MOZ_ASSERT((bitspertexel % 8) == 0); // That would only happen for
                                         // compressed images, which cannot use
                                         // deferred initialization.
    size_t bytespertexel = bitspertexel / 8;
    CheckedUint32 checked_byteLength
        = WebGLContext::GetImageSize(
                        imageInfo.mHeight,
                        imageInfo.mWidth,
                        imageInfo.mDepth,
                        bytespertexel,
                        mContext->mPixelStoreUnpackAlignment);
    MOZ_ASSERT(checked_byteLength.isValid()); // Should have been checked
                                              // earlier.

    size_t byteCount = checked_byteLength.value();

    UniquePtr<uint8_t> zeros((uint8_t*)calloc(1, byteCount));
    if (zeros == nullptr) {
        // Failed to allocate memory. Lose the context. Return OOM error.
        mContext->ForceLoseContext(true);
        mContext->ErrorOutOfMemory("EnsureInitializedImageData: Failed to alloc %u "
                                   "bytes to clear image target `%s` level `%d`.",
                                   byteCount, mContext->EnumName(imageTarget.get()),
                                   level);
         return false;
    }

    gl::GLContext* gl = mContext->gl;
    gl::ScopedBindTexture autoBindTex(gl, mGLName, mTarget);

    GLenum driverInternalFormat = LOCAL_GL_NONE;
    GLenum driverFormat = LOCAL_GL_NONE;
    GLenum driverType = LOCAL_GL_NONE;
    DriverFormatsFromEffectiveInternalFormat(gl,
                                             imageInfo.mEffectiveInternalFormat,
                                             &driverInternalFormat,
                                             &driverFormat, &driverType);

    mContext->GetAndFlushUnderlyingGLErrors();
    if (imageTarget == LOCAL_GL_TEXTURE_3D) {
        MOZ_ASSERT(mImmutable,
                   "Shouldn't be possible to have non-immutable-format 3D"
                   " textures in WebGL");
        gl->fTexSubImage3D(imageTarget.get(), level, 0, 0, 0, imageInfo.mWidth,
                           imageInfo.mHeight, imageInfo.mDepth, driverFormat,
                           driverType, zeros.get());
    } else {
        if (mImmutable) {
            gl->fTexSubImage2D(imageTarget.get(), level, 0, 0, imageInfo.mWidth,
                               imageInfo.mHeight, driverFormat, driverType,
                               zeros.get());
        } else {
            gl->fTexImage2D(imageTarget.get(), level, driverInternalFormat,
                            imageInfo.mWidth, imageInfo.mHeight, 0,
                            driverFormat, driverType, zeros.get());
        }
    }
    GLenum error = mContext->GetAndFlushUnderlyingGLErrors();
    if (error) {
        // Should only be OUT_OF_MEMORY. Anyway, there's no good way to recover
        // from this here.
        gfxCriticalError() << "GL context GetAndFlushUnderlyingGLErrors " << gfx::hexa(error);
        printf_stderr("Error: 0x%4x\n", error);
        if (error != LOCAL_GL_OUT_OF_MEMORY) {
            // Errors on texture upload have been related to video
            // memory exposure in the past, which is a security issue.
            // Force loss of context.
            mContext->ForceLoseContext(true);
            return false;
        }

        // Out-of-memory uploading pixels to GL. Lose context and report OOM.
        mContext->ForceLoseContext(true);
        mContext->ErrorOutOfMemory("EnsureNoUninitializedImageData: Failed to "
                                   "upload texture of width: %u, height: %u, "
                                   "depth: %u to target %s level %d.",
                                   imageInfo.mWidth, imageInfo.mHeight, imageInfo.mDepth,
                                   mContext->EnumName(imageTarget.get()), level);
        return false;
    }

    SetImageDataStatus(imageTarget, level, WebGLImageDataStatus::InitializedImageData);

    return true;
}
Exemplo n.º 14
0
void
WebGLTexture::EnsureNoUninitializedImageData(TexImageTarget imageTarget, GLint level)
{
    const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level);
    if (!imageInfo.HasUninitializedImageData())
        return;

    mContext->MakeContextCurrent();

    // Try to clear with glCLear.

    if (imageTarget == LOCAL_GL_TEXTURE_2D) {
        bool cleared = ClearWithTempFB(mContext, GLName(),
                                       imageTarget, level,
                                       imageInfo.mEffectiveInternalFormat,
                                       imageInfo.mHeight, imageInfo.mWidth);
        if (cleared) {
            SetImageDataStatus(imageTarget, level, WebGLImageDataStatus::InitializedImageData);
            return;
        }
    }

    // That didn't work. Try uploading zeros then.
    gl::ScopedBindTexture autoBindTex(mContext->gl, GLName(), mTarget.get());

    size_t bitspertexel = GetBitsPerTexel(imageInfo.mEffectiveInternalFormat);
    MOZ_ASSERT((bitspertexel % 8) == 0); // that would only happen for compressed images, which
                                         // cannot use deferred initialization.
    size_t bytespertexel = bitspertexel / 8;
    CheckedUint32 checked_byteLength
        = WebGLContext::GetImageSize(
                        imageInfo.mHeight,
                        imageInfo.mWidth,
                        imageInfo.mDepth,
                        bytespertexel,
                        mContext->mPixelStoreUnpackAlignment);
    MOZ_ASSERT(checked_byteLength.isValid()); // should have been checked earlier

    UniquePtr<uint8_t> zeros((uint8_t*)moz_xcalloc(1, checked_byteLength.value())); // Infallible for now.

    gl::GLContext* gl = mContext->gl;
    GLenum driverInternalFormat = LOCAL_GL_NONE;
    GLenum driverFormat = LOCAL_GL_NONE;
    GLenum driverType = LOCAL_GL_NONE;
    DriverFormatsFromEffectiveInternalFormat(gl, imageInfo.mEffectiveInternalFormat,
                                             &driverInternalFormat, &driverFormat, &driverType);

    mContext->GetAndFlushUnderlyingGLErrors();
    if (imageTarget == LOCAL_GL_TEXTURE_3D) {
        MOZ_ASSERT(mImmutable, "Shouldn't be possible to have non-immutable-format 3D textures in WebGL");
        gl->fTexSubImage3D(imageTarget.get(), level, 0, 0, 0,
                        imageInfo.mWidth, imageInfo.mHeight, imageInfo.mDepth,
                        driverFormat, driverType,
                        zeros.get());
    } else {
        if (mImmutable) {
            gl->fTexSubImage2D(imageTarget.get(), level, 0, 0,
                            imageInfo.mWidth, imageInfo.mHeight,
                            driverFormat, driverType,
                            zeros.get());
        } else {
            gl->fTexImage2D(imageTarget.get(), level, driverInternalFormat,
                            imageInfo.mWidth, imageInfo.mHeight,
                            0, driverFormat, driverType,
                            zeros.get());
        }
    }
    GLenum error = mContext->GetAndFlushUnderlyingGLErrors();
    if (error) {
        // Should only be OUT_OF_MEMORY. Anyway, there's no good way to recover from this here.
        printf_stderr("Error: 0x%4x\n", error);
        MOZ_CRASH(); // errors on texture upload have been related to video memory exposure in the past.
    }

    SetImageDataStatus(imageTarget, level, WebGLImageDataStatus::InitializedImageData);
}
Exemplo n.º 15
0
static bool ZeroTextureData(const WebGLContext* webgl, GLuint tex,
                            TexImageTarget target, uint32_t level,
                            const webgl::FormatUsageInfo* usage, uint32_t width,
                            uint32_t height, uint32_t depth) {
  // This has two usecases:
  // 1. Lazy zeroing of uninitialized textures:
  //    a. Before draw.
  //    b. Before partial upload. (TexStorage + TexSubImage)
  // 2. Zero subrects from out-of-bounds blits. (CopyTex(Sub)Image)

  // We have no sympathy for any of these cases.

  // "Doctor, it hurts when I do this!" "Well don't do that!"
  const auto targetStr = EnumString(target.get());
  webgl->GeneratePerfWarning(
      "Tex image %s level %u is incurring lazy initialization.",
      targetStr.c_str(), level);

  gl::GLContext* gl = webgl->GL();

  GLenum scopeBindTarget;
  switch (target.get()) {
    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
      scopeBindTarget = LOCAL_GL_TEXTURE_CUBE_MAP;
      break;
    default:
      scopeBindTarget = target.get();
      break;
  }
  const gl::ScopedBindTexture scopeBindTexture(gl, tex, scopeBindTarget);
  const auto& compression = usage->format->compression;
  if (compression) {
    auto sizedFormat = usage->format->sizedFormat;
    MOZ_RELEASE_ASSERT(sizedFormat, "GFX: texture sized format not set");

    const auto fnSizeInBlocks = [](CheckedUint32 pixels,
                                   uint8_t pixelsPerBlock) {
      return RoundUpToMultipleOf(pixels, pixelsPerBlock) / pixelsPerBlock;
    };

    const auto widthBlocks = fnSizeInBlocks(width, compression->blockWidth);
    const auto heightBlocks = fnSizeInBlocks(height, compression->blockHeight);

    CheckedUint32 checkedByteCount = compression->bytesPerBlock;
    checkedByteCount *= widthBlocks;
    checkedByteCount *= heightBlocks;
    checkedByteCount *= depth;

    if (!checkedByteCount.isValid()) return false;

    const size_t byteCount = checkedByteCount.value();

    UniqueBuffer zeros = calloc(1, byteCount);
    if (!zeros) return false;

    ScopedUnpackReset scopedReset(webgl);
    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1);  // Don't bother with
                                                     // striding it well.

    const auto error =
        DoCompressedTexSubImage(gl, target.get(), level, 0, 0, 0, width, height,
                                depth, sizedFormat, byteCount, zeros.get());
    return !error;
  }

  const auto driverUnpackInfo = usage->idealUnpack;
  MOZ_RELEASE_ASSERT(driverUnpackInfo, "GFX: ideal unpack info not set.");

  if (usage->format->d) {
    // ANGLE_depth_texture does not allow uploads, so we have to clear.
    // (Restriction because of D3D9)
    // Also, depth resources are cleared to 1.0f and are always renderable, so
    // just use FB clears.
    return ClearDepthTexture(*webgl, tex, target, level, usage, depth);
  }

  const webgl::PackingInfo packing = driverUnpackInfo->ToPacking();

  const auto bytesPerPixel = webgl::BytesPerPixel(packing);

  CheckedUint32 checkedByteCount = bytesPerPixel;
  checkedByteCount *= width;
  checkedByteCount *= height;
  checkedByteCount *= depth;

  if (!checkedByteCount.isValid()) return false;

  const size_t byteCount = checkedByteCount.value();

  UniqueBuffer zeros = calloc(1, byteCount);
  if (!zeros) return false;

  ScopedUnpackReset scopedReset(webgl);
  gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
                   1);  // Don't bother with striding it well.
  const auto error = DoTexSubImage(gl, target, level, 0, 0, 0, width, height,
                                   depth, packing, zeros.get());
  return !error;
}
Exemplo n.º 16
0
/*static*/ bool
TexUnpackSurface::UploadDataSurface(bool isSubImage, WebGLContext* webgl,
                                    TexImageTarget target, GLint level,
                                    const webgl::DriverUnpackInfo* dui, GLint xOffset,
                                    GLint yOffset, GLint zOffset, GLsizei width,
                                    GLsizei height, gfx::DataSourceSurface* surf,
                                    bool isSurfAlphaPremult, GLenum* const out_glError)
{
    gl::GLContext* gl = webgl->GL();
    MOZ_ASSERT(gl->IsCurrent());
    *out_glError = 0;

    if (isSurfAlphaPremult != webgl->mPixelStore_PremultiplyAlpha)
        return false;

    gl::OriginPos srcOrigin, dstOrigin;
    OriginsForDOM(webgl, &srcOrigin, &dstOrigin);
    if (srcOrigin != dstOrigin)
        return false;

    // This differs from the raw-data upload in that we choose how we do the unpack.
    // (alignment, etc.)

    // Uploading RGBX as RGBA and blitting to RGB is faster than repacking RGBX into
    // RGB on the CPU. However, this is optimization is out-of-scope for now.

    static const webgl::DriverUnpackInfo kInfoBGRA = {
        LOCAL_GL_BGRA,
        LOCAL_GL_BGRA,
        LOCAL_GL_UNSIGNED_BYTE,
    };

    const webgl::DriverUnpackInfo* chosenDUI = nullptr;

    switch (surf->GetFormat()) {
    case gfx::SurfaceFormat::B8G8R8A8:
        if (SupportsBGRA(gl) &&
            dui->internalFormat == LOCAL_GL_RGBA &&
            dui->unpackFormat == LOCAL_GL_RGBA &&
            dui->unpackType == LOCAL_GL_UNSIGNED_BYTE)
        {
            chosenDUI = &kInfoBGRA;
        }
        break;

    case gfx::SurfaceFormat::R8G8B8A8:
        if (dui->unpackFormat == LOCAL_GL_RGBA &&
            dui->unpackType == LOCAL_GL_UNSIGNED_BYTE)
        {
            chosenDUI = dui;
        }
        break;

    case gfx::SurfaceFormat::R5G6B5_UINT16:
        if (dui->unpackFormat == LOCAL_GL_RGB &&
            dui->unpackType == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
        {
            chosenDUI = dui;
        }
        break;

    default:
        break;
    }

    if (!chosenDUI)
        return false;

    gfx::DataSourceSurface::ScopedMap map(surf, gfx::DataSourceSurface::MapType::READ);
    if (!map.IsMapped())
        return false;

    const webgl::PackingInfo pi = {chosenDUI->unpackFormat, chosenDUI->unpackType};
    const auto bytesPerPixel = webgl::BytesPerPixel(pi);
    const size_t bytesPerRow = width * bytesPerPixel;

    const GLint kMaxUnpackAlignment = 8;
    size_t unpackAlignment;
    if (!GuessAlignment(map.GetData(), bytesPerRow, map.GetStride(), kMaxUnpackAlignment,
                        &unpackAlignment))
    {
        return false;
        // TODO: Consider using UNPACK_ settings to set the stride based on the too-large
        // alignment used for some SourceSurfaces. (D2D allegedy likes alignment=16)
    }

    MOZ_ALWAYS_TRUE( webgl->gl->MakeCurrent() );
    ScopedUnpackReset scopedReset(webgl);
    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, unpackAlignment);

    const GLsizei depth = 1;
    GLenum error = DoTexOrSubImage(isSubImage, gl, target.get(), level, chosenDUI,
                                   xOffset, yOffset, zOffset, width, height, depth,
                                   map.GetData());
    if (error) {
        *out_glError = error;
        return false;
    }

    return true;
}
Exemplo n.º 17
0
void
TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
                              WebGLTexture* tex, TexImageTarget target, GLint level,
                              const webgl::DriverUnpackInfo* dui, GLint xOffset,
                              GLint yOffset, GLint zOffset, GLenum* const out_glError)
{
    MOZ_ASSERT_IF(needsRespec, !isSubImage);
    *out_glError = 0;

    WebGLContext* webgl = tex->mContext;

    gl::GLContext* gl = webgl->GL();
    gl->MakeCurrent();

    if (needsRespec) {
        GLenum error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dui, xOffset,
                                       yOffset, zOffset, mWidth, mHeight, mDepth,
                                       nullptr);
        if (error) {
            MOZ_ASSERT(!error);
            *out_glError = LOCAL_GL_OUT_OF_MEMORY;
            return;
        }
    }

    do {
        if (dui->unpackFormat != LOCAL_GL_RGB && dui->unpackFormat != LOCAL_GL_RGBA)
            break;

        if (dui->unpackType != LOCAL_GL_UNSIGNED_BYTE)
            break;

        gl::ScopedFramebuffer scopedFB(gl);
        gl::ScopedBindFramebuffer bindFB(gl, scopedFB.FB());

        {
            gl::GLContext::LocalErrorScope errorScope(*gl);

            gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
                                      target.get(), tex->mGLName, level);

            if (errorScope.GetError())
                break;
        }

        const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
        if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
            break;

        gl::OriginPos srcOrigin, dstOrigin;
        OriginsForDOM(webgl, &srcOrigin, &dstOrigin);

        const gfx::IntSize destSize(mWidth, mHeight);
        if (!gl->BlitHelper()->BlitImageToFramebuffer(mImage, destSize, scopedFB.FB(),
                                                      dstOrigin))
        {
            break;
        }

        return; // Blitting was successful, so we're done!
    } while (false);

    TexUnpackSurface surfBlob(mImage->GetAsSourceSurface(), mIsAlphaPremult);

    surfBlob.TexOrSubImage(isSubImage, needsRespec, funcName, tex, target, level, dui,
                           xOffset, yOffset, zOffset, out_glError);
}