Commit 69d3f064 authored by Brandon Jones's avatar Brandon Jones Committed by Commit Bot

Ensured XRWebGLLayers clamp their framebuffer size.

In some cases extreme output canvas sized were causing failed
allocations and incomplete framebuffers, which made the ImageLayerBridge
choke. This patch both clamps the backbuffer size to the max texture
size and, if an incomplete framebuffer is detected, produces black 1x1
images for the ImageLayerBridge to consume instead of attempting to pass
the texture that failed to allocate.

Bug: 814460
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.linux:linux_layout_tests_slimming_paint_v2
Change-Id: Idba50e08052767360423018e06bc65f1f87c4d14
Reviewed-on: https://chromium-review.googlesource.com/964796Reviewed-by: default avatarBrian Sheedy <bsheedy@chromium.org>
Reviewed-by: default avatarIan Vollick <vollick@chromium.org>
Commit-Queue: Brandon Jones <bajones@chromium.org>
Cr-Commit-Position: refs/heads/master@{#543549}
parent cfb85583
<!DOCTYPE html>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
<script src="../xr/resources/xr-device-mocking.js"></script>
<script src="../xr/resources/xr-test-utils.js"></script>
<script src="../xr/resources/test-constants.js"></script>
<canvas id="webgl-canvas"></canvas>
<script>
let fakeDevices = fakeXRDevices();
let outputContext = getOutputContext();
let outputCanvas = outputContext.canvas;
// Make an unreasonably large magic window canvas.
outputCanvas.width = 18000; // 16k ought to be a large enough max for anyone.
outputCanvas.height = 20000;
let outputCanvasRatio = outputCanvas.width / outputCanvas.height;
xr_session_promise_test( (session, t) => new Promise((resolve, reject) => {
let webglLayer = new XRWebGLLayer(session, gl);
session.baseLayer = webglLayer;
t.step(() => {
// The layer's framebuffer should be smaller than the requested size.
assert_true(webglLayer.framebufferWidth < outputCanvas.width);
assert_true(webglLayer.framebufferHeight < outputCanvas.height);
// The layer's dimensions should keep the same ratio as the canvas.
let framebufferRatio = webglLayer.framebufferWidth / webglLayer.framebufferHeight;
assert_approx_equals(framebufferRatio, outputCanvasRatio, 0.0001);
});
// Resize the canvas to something more reasonable.
outputCanvas.width = 256;
outputCanvas.height = 128;
// Give the UA a chance to respond to the resize.
setTimeout(() => {
// Check to ensure the framebuffer resized to match the new canvas dimensions.
t.step(() => {
assert_equals(webglLayer.framebufferWidth, outputCanvas.width);
assert_equals(webglLayer.framebufferHeight, outputCanvas.height);
});
resolve();
}, 100);
}), fakeDevices["FakeGooglePixelPhone"], [{ outputContext: outputContext }],
"Ensure a WebGL layer's framebuffer size is adjusted appropriately when a large canvas is requested");
</script>
......@@ -121,6 +121,8 @@ bool XRWebGLDrawingBuffer::Initialize(const IntSize& size,
std::unique_ptr<Extensions3DUtil> extensions_util =
Extensions3DUtil::Create(gl);
gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size_);
// Check context capabilities
int max_sample_count = 0;
anti_aliasing_mode_ = kNone;
......@@ -161,10 +163,27 @@ bool XRWebGLDrawingBuffer::ContextLost() {
return drawing_buffer_->destroyed();
}
void XRWebGLDrawingBuffer::Resize(const IntSize& new_size) {
IntSize XRWebGLDrawingBuffer::AdjustSize(const IntSize& new_size) {
// Ensure we always have at least a 1x1 buffer
IntSize adjusted_size(std::max(1, new_size.Width()),
std::max(1, new_size.Height()));
float width = std::max(1, new_size.Width());
float height = std::max(1, new_size.Height());
float adjusted_scale =
std::min(static_cast<float>(max_texture_size_) / width,
static_cast<float>(max_texture_size_) / height);
// Clamp if the desired size is greater than the maximum texture size for the
// device. Scale both dimensions proportionally so that we avoid stretching.
if (adjusted_scale < 1.0f) {
width *= adjusted_scale;
height *= adjusted_scale;
}
return IntSize(width, height);
}
void XRWebGLDrawingBuffer::Resize(const IntSize& new_size) {
IntSize adjusted_size = AdjustSize(new_size);
if (adjusted_size == size_)
return;
......@@ -177,6 +196,10 @@ void XRWebGLDrawingBuffer::Resize(const IntSize& new_size) {
size_ = adjusted_size;
// Free all mailboxes, because they are now of the wrong size. Only the
// first call in this loop has any effect.
recycled_color_buffer_queue_.clear();
gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
// Provide a depth and/or stencil buffer if requested.
......@@ -245,6 +268,9 @@ void XRWebGLDrawingBuffer::Resize(const IntSize& new_size) {
if (gl->CheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
DLOG(ERROR) << "Framebuffer incomplete";
framebuffer_incomplete_ = true;
} else {
framebuffer_incomplete_ = false;
}
DrawingBuffer::Client* client = drawing_buffer_->client();
......@@ -341,10 +367,17 @@ void XRWebGLDrawingBuffer::SwapColorBuffers() {
GL_TEXTURE_2D, back_color_buffer_->texture_id, 0);
}
if (discard_framebuffer_supported_) {
const GLenum kAttachments[3] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT,
GL_STENCIL_ATTACHMENT};
gl->DiscardFramebufferEXT(GL_FRAMEBUFFER, 3, kAttachments);
if (gl->CheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
DLOG(ERROR) << "Framebuffer incomplete";
framebuffer_incomplete_ = true;
} else {
framebuffer_incomplete_ = false;
if (discard_framebuffer_supported_) {
const GLenum kAttachments[3] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT,
GL_STENCIL_ATTACHMENT};
gl->DiscardFramebufferEXT(GL_FRAMEBUFFER, 3, kAttachments);
}
}
client->DrawingBufferClientRestoreFramebufferBinding();
......@@ -357,8 +390,9 @@ XRWebGLDrawingBuffer::TransferToStaticBitmapImage(
scoped_refptr<ColorBuffer> buffer;
bool success = false;
// Ensure the context isn't lost before continuing.
if (!ContextLost()) {
// Ensure the context isn't lost and the framebuffer is complete before
// continuing.
if (!ContextLost() && !framebuffer_incomplete_) {
SwapColorBuffers();
buffer = front_color_buffer_;
......@@ -375,8 +409,9 @@ XRWebGLDrawingBuffer::TransferToStaticBitmapImage(
if (!success) {
// If we can't get a mailbox, return an transparent black ImageBitmap.
// The only situation in which this could happen is when two or more calls
// to transferToImageBitmap are made back-to-back, or when the context gets
// lost.
// to transferToImageBitmap are made back-to-back, if the framebuffer is
// incomplete (likely due to a failed buffer allocation), or when the
// context gets lost.
sk_sp<SkSurface> surface =
SkSurface::MakeRasterN32Premul(size_.Width(), size_.Height());
return StaticBitmapImage::Create(surface->makeImageSnapshot());
......
......@@ -84,6 +84,8 @@ class PLATFORM_EXPORT XRWebGLDrawingBuffer
bool Initialize(const IntSize&, bool use_multisampling, bool use_multiview);
IntSize AdjustSize(const IntSize&);
scoped_refptr<ColorBuffer> CreateColorBuffer();
scoped_refptr<ColorBuffer> CreateOrRecycleColorBuffer();
......@@ -124,7 +126,9 @@ class PLATFORM_EXPORT XRWebGLDrawingBuffer
AntialiasingMode anti_aliasing_mode_ = kNone;
bool storage_texture_supported_ = false;
int max_texture_size_ = 0;
int sample_count_ = 0;
bool framebuffer_incomplete_ = false;
};
} // namespace blink
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment