// Copyright 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "cc/direct_renderer.h" #include #include "base/debug/trace_event.h" #include "base/metrics/histogram.h" #include "cc/draw_quad.h" #include "cc/math_util.h" #include "ui/gfx/rect_conversions.h" #include "ui/gfx/transform.h" static gfx::Transform orthoProjectionMatrix(float left, float right, float bottom, float top) { // Use the standard formula to map the clipping frustum to the cube from // [-1, -1, -1] to [1, 1, 1]. float deltaX = right - left; float deltaY = top - bottom; gfx::Transform proj; if (!deltaX || !deltaY) return proj; proj.matrix().setDouble(0, 0, 2.0f / deltaX); proj.matrix().setDouble(0, 3, -(right + left) / deltaX); proj.matrix().setDouble(1, 1, 2.0f / deltaY); proj.matrix().setDouble(1, 3, -(top + bottom) / deltaY); // Z component of vertices is always set to zero as we don't use the depth buffer // while drawing. proj.matrix().setDouble(2, 2, 0); return proj; } static gfx::Transform windowMatrix(int x, int y, int width, int height) { gfx::Transform canvas; // Map to window position and scale up to pixel coordinates. canvas.Translate3d(x, y, 0); canvas.Scale3d(width, height, 0); // Map from ([-1, -1] to [1, 1]) -> ([0, 0] to [1, 1]) canvas.Translate3d(0.5, 0.5, 0.5); canvas.Scale3d(0.5, 0.5, 0.5); return canvas; } namespace cc { DirectRenderer::DrawingFrame::DrawingFrame() : rootRenderPass(0) , currentRenderPass(0) , currentTexture(0) , flippedY(false) { } DirectRenderer::DrawingFrame::~DrawingFrame() { } // // static gfx::RectF DirectRenderer::quadVertexRect() { return gfx::RectF(-0.5, -0.5, 1, 1); } // static void DirectRenderer::quadRectTransform(gfx::Transform* quadRectTransform, const gfx::Transform& quadTransform, const gfx::RectF& quadRect) { *quadRectTransform = quadTransform; quadRectTransform->Translate(0.5 * quadRect.width() + quadRect.x(), 0.5 * quadRect.height() + quadRect.y()); quadRectTransform->Scale(quadRect.width(), quadRect.height()); } // static void DirectRenderer::initializeMatrices(DrawingFrame& frame, const gfx::Rect& drawRect, bool flipY) { if (flipY) frame.projectionMatrix = orthoProjectionMatrix(drawRect.x(), drawRect.right(), drawRect.bottom(), drawRect.y()); else frame.projectionMatrix = orthoProjectionMatrix(drawRect.x(), drawRect.right(), drawRect.y(), drawRect.bottom()); frame.windowMatrix = windowMatrix(0, 0, drawRect.width(), drawRect.height()); frame.flippedY = flipY; } // static gfx::Rect DirectRenderer::moveScissorToWindowSpace(const DrawingFrame& frame, gfx::RectF scissorRect) { gfx::Rect scissorRectInCanvasSpace = gfx::ToEnclosingRect(scissorRect); // The scissor coordinates must be supplied in viewport space so we need to offset // by the relative position of the top left corner of the current render pass. gfx::Rect framebufferOutputRect = frame.currentRenderPass->output_rect; scissorRectInCanvasSpace.set_x(scissorRectInCanvasSpace.x() - framebufferOutputRect.x()); if (frame.flippedY && !frame.currentTexture) scissorRectInCanvasSpace.set_y(framebufferOutputRect.height() - (scissorRectInCanvasSpace.bottom() - framebufferOutputRect.y())); else scissorRectInCanvasSpace.set_y(scissorRectInCanvasSpace.y() - framebufferOutputRect.y()); return scissorRectInCanvasSpace; } DirectRenderer::DirectRenderer(RendererClient* client, ResourceProvider* resourceProvider) : Renderer(client) , m_resourceProvider(resourceProvider) { } DirectRenderer::~DirectRenderer() { } void DirectRenderer::setEnlargePassTextureAmountForTesting(gfx::Vector2d amount) { m_enlargePassTextureAmount = amount; } void DirectRenderer::decideRenderPassAllocationsForFrame(const RenderPassList& renderPassesInDrawOrder) { base::hash_map renderPassesInFrame; for (size_t i = 0; i < renderPassesInDrawOrder.size(); ++i) renderPassesInFrame.insert(std::pair(renderPassesInDrawOrder[i]->id, renderPassesInDrawOrder[i])); std::vector passesToDelete; ScopedPtrHashMap::const_iterator passIterator; for (passIterator = m_renderPassTextures.begin(); passIterator != m_renderPassTextures.end(); ++passIterator) { base::hash_map::const_iterator it = renderPassesInFrame.find(passIterator->first); if (it == renderPassesInFrame.end()) { passesToDelete.push_back(passIterator->first); continue; } const RenderPass* renderPassInFrame = it->second; const gfx::Size& requiredSize = renderPassTextureSize(renderPassInFrame); GLenum requiredFormat = renderPassTextureFormat(renderPassInFrame); CachedResource* texture = passIterator->second; DCHECK(texture); bool sizeAppropriate = texture->size().width() >= requiredSize.width() && texture->size().height() >= requiredSize.height(); if (texture->id() && (!sizeAppropriate || texture->format() != requiredFormat)) texture->Free(); } // Delete RenderPass textures from the previous frame that will not be used again. for (size_t i = 0; i < passesToDelete.size(); ++i) m_renderPassTextures.erase(passesToDelete[i]); for (size_t i = 0; i < renderPassesInDrawOrder.size(); ++i) { if (!m_renderPassTextures.contains(renderPassesInDrawOrder[i]->id)) { scoped_ptr texture = CachedResource::create(m_resourceProvider); m_renderPassTextures.set(renderPassesInDrawOrder[i]->id, texture.Pass()); } } } void DirectRenderer::drawFrame(RenderPassList& renderPassesInDrawOrder) { TRACE_EVENT0("cc", "DirectRenderer::drawFrame"); UMA_HISTOGRAM_COUNTS("Renderer4.renderPassCount", renderPassesInDrawOrder.size()); const RenderPass* rootRenderPass = renderPassesInDrawOrder.back(); DCHECK(rootRenderPass); DrawingFrame frame; frame.rootRenderPass = rootRenderPass; frame.rootDamageRect = capabilities().usingPartialSwap ? rootRenderPass->damage_rect : rootRenderPass->output_rect; frame.rootDamageRect.Intersect(gfx::Rect(gfx::Point(), viewportSize())); beginDrawingFrame(frame); for (size_t i = 0; i < renderPassesInDrawOrder.size(); ++i) drawRenderPass(frame, renderPassesInDrawOrder[i]); finishDrawingFrame(frame); renderPassesInDrawOrder.clear(); } gfx::RectF DirectRenderer::computeScissorRectForRenderPass(const DrawingFrame& frame) { gfx::RectF renderPassScissor = frame.currentRenderPass->output_rect; if (frame.rootDamageRect == frame.rootRenderPass->output_rect) return renderPassScissor; gfx::Transform inverseTransform(gfx::Transform::kSkipInitialization); if (frame.currentRenderPass->transform_to_root_target.GetInverse(&inverseTransform)) { // Only intersect inverse-projected damage if the transform is invertible. gfx::RectF damageRectInRenderPassSpace = MathUtil::projectClippedRect(inverseTransform, frame.rootDamageRect); renderPassScissor.Intersect(damageRectInRenderPassSpace); } return renderPassScissor; } void DirectRenderer::setScissorStateForQuad(const DrawingFrame& frame, const DrawQuad& quad) { if (quad.isClipped()) { gfx::RectF quadScissorRect = quad.clipRect(); setScissorTestRect(moveScissorToWindowSpace(frame, quadScissorRect)); } else ensureScissorTestDisabled(); } void DirectRenderer::setScissorStateForQuadWithRenderPassScissor(const DrawingFrame& frame, const DrawQuad& quad, const gfx::RectF& renderPassScissor, bool* shouldSkipQuad) { gfx::RectF quadScissorRect = renderPassScissor; if (quad.isClipped()) quadScissorRect.Intersect(quad.clipRect()); if (quadScissorRect.IsEmpty()) { *shouldSkipQuad = true; return; } *shouldSkipQuad = false; setScissorTestRect(moveScissorToWindowSpace(frame, quadScissorRect)); } void DirectRenderer::finishDrawingQuadList() { } void DirectRenderer::drawRenderPass(DrawingFrame& frame, const RenderPass* renderPass) { TRACE_EVENT0("cc", "DirectRenderer::drawRenderPass"); if (!useRenderPass(frame, renderPass)) return; bool usingScissorAsOptimization = capabilities().usingPartialSwap; gfx::RectF renderPassScissor; if (usingScissorAsOptimization) { renderPassScissor = computeScissorRectForRenderPass(frame); setScissorTestRect(moveScissorToWindowSpace(frame, renderPassScissor)); } if (frame.currentRenderPass != frame.rootRenderPass || m_client->shouldClearRootRenderPass()) { if (!usingScissorAsOptimization) ensureScissorTestDisabled(); clearFramebuffer(frame); } const QuadList& quadList = renderPass->quad_list; for (QuadList::constBackToFrontIterator it = quadList.backToFrontBegin(); it != quadList.backToFrontEnd(); ++it) { const DrawQuad& quad = *(*it); bool shouldSkipQuad = false; if (usingScissorAsOptimization) setScissorStateForQuadWithRenderPassScissor(frame, quad, renderPassScissor, &shouldSkipQuad); else setScissorStateForQuad(frame, quad); if (!shouldSkipQuad) drawQuad(frame, *it); } finishDrawingQuadList(); CachedResource* texture = m_renderPassTextures.get(renderPass->id); if (texture) texture->setIsComplete(!renderPass->has_occlusion_from_outside_target_surface); } bool DirectRenderer::useRenderPass(DrawingFrame& frame, const RenderPass* renderPass) { frame.currentRenderPass = renderPass; frame.currentTexture = 0; if (renderPass == frame.rootRenderPass) { bindFramebufferToOutputSurface(frame); initializeMatrices(frame, renderPass->output_rect, flippedFramebuffer()); setDrawViewportSize(renderPass->output_rect.size()); return true; } CachedResource* texture = m_renderPassTextures.get(renderPass->id); DCHECK(texture); gfx::Size size = renderPassTextureSize(renderPass); size.Enlarge(m_enlargePassTextureAmount.x(), m_enlargePassTextureAmount.y()); if (!texture->id() && !texture->Allocate(size, renderPassTextureFormat(renderPass), ResourceProvider::TextureUsageFramebuffer)) return false; return bindFramebufferToTexture(frame, texture, renderPass->output_rect); } bool DirectRenderer::haveCachedResourcesForRenderPassId(RenderPass::Id id) const { CachedResource* texture = m_renderPassTextures.get(id); return texture && texture->id() && texture->isComplete(); } // static gfx::Size DirectRenderer::renderPassTextureSize(const RenderPass* pass) { return pass->output_rect.size(); } // static GLenum DirectRenderer::renderPassTextureFormat(const RenderPass*) { return GL_RGBA; } } // namespace cc