157 scaleTx.setToScale(width / 2.0, -height / 2.0, 1); 158 scaleTx.translate(1, -1); 159 projTx.mul(scaleTx); 160 viewProjTx = camera.getProjViewTx(viewProjTx); 161 projTx.mul(viewProjTx); 162 } 163 } 164 165 protected void paintImpl(final Graphics backBufferGraphics) { 166 // We should not be painting anything with a width / height 167 // that is <= 0, so we might as well bail right off. 168 if (width <= 0 || height <= 0 || backBufferGraphics == null) { 169 root.renderForcedContent(backBufferGraphics); 170 return; 171 } 172 173 // This "g" variable might represent the back buffer graphics, or it 174 // might be reassigned to the sceneBuffer graphics. 175 Graphics g = backBufferGraphics; 176 // Take into account the pixel scale factor for retina displays 177 final float pixelScale = getPixelScaleFactor(); 178 // Cache pixelScale in Graphics for use in 3D shaders such as camera and light positions. 179 g.setPixelScaleFactor(pixelScale); 180 181 // Initialize renderEverything based on various conditions that will cause us to render 182 // the entire scene every time. 183 boolean renderEverything = overlayRoot != null || 184 freshBackBuffer || 185 sceneState.getScene().isEntireSceneDirty() || 186 sceneState.getScene().getDepthBuffer() || 187 !PrismSettings.dirtyOptsEnabled; 188 // We are going to draw dirty opt boxes either if we're supposed to show the dirty 189 // regions, or if we're supposed to show the overdraw boxes. 190 final boolean showDirtyOpts = PrismSettings.showDirtyRegions || PrismSettings.showOverdraw; 191 // If showDirtyOpts is turned on and we're not using a depth buffer 192 // then we will render the scene to an intermediate texture, and then at the end we'll 193 // draw that intermediate texture to the back buffer. 194 if (showDirtyOpts && !sceneState.getScene().getDepthBuffer()) { 195 final int bufferWidth = (int) Math.ceil(width * pixelScale); 196 final int bufferHeight = (int) Math.ceil(height * pixelScale); 197 // Check whether the sceneBuffer texture needs to be reconstructed 198 if (sceneBuffer != null) { 199 sceneBuffer.lock(); 200 if (sceneBuffer.isSurfaceLost() || 201 bufferWidth != sceneBuffer.getContentWidth() || 202 bufferHeight != sceneBuffer.getContentHeight()) { 203 sceneBuffer.unlock(); 204 sceneBuffer.dispose(); 205 sceneBuffer = null; 206 } 207 } 208 // If sceneBuffer is null, we need to create a new texture. In this 209 // case we will also need to render the whole scene (so don't bother 210 // with dirty opts) 211 if (sceneBuffer == null) { 212 sceneBuffer = g.getResourceFactory().createRTTexture( 213 bufferWidth, 214 bufferHeight, 215 Texture.WrapMode.CLAMP_TO_ZERO, 216 false); 217 renderEverything = true; 218 } 219 sceneBuffer.contentsUseful(); 220 // Hijack the "g" graphics variable 221 g = sceneBuffer.createGraphics(); 222 g.scale(pixelScale, pixelScale); 223 } else if (sceneBuffer != null) { 224 // We're in a situation where we have previously rendered to the sceneBuffer, but in 225 // this render pass for whatever reason we're going to draw directly to the back buffer. 226 // In this case we need to release the sceneBuffer. 227 sceneBuffer.dispose(); 228 sceneBuffer = null; 229 } 230 231 // The status will be set only if we're rendering with dirty regions 232 int status = -1; 233 234 // If we're rendering with dirty regions, then we'll call the root node to accumulate 235 // the dirty regions and then again to do the pre culling. 236 if (!renderEverything) { 237 if (PULSE_LOGGING_ENABLED) { 238 PulseLogger.newPhase("Dirty Opts Computed"); 239 } 240 clip.setBounds(0, 0, width, height); 241 dirtyRegionTemp.makeEmpty(); 242 dirtyRegionContainer.reset(); 295 if (!nodePath.isEmpty()) { 296 roots.add(nodePath.last()); 297 } 298 } 299 } 300 root.printDirtyOpts(s, roots); 301 PulseLogger.addMessage(s.toString()); 302 } 303 304 // Paint each dirty region 305 for (int i = 0; i < dirtyRegionSize; ++i) { 306 final RectBounds dirtyRegion = dirtyRegionContainer.getDirtyRegion(i); 307 // TODO it should be impossible to have ever created a dirty region that was empty... 308 // Make sure we are not trying to render in some invalid region 309 if (dirtyRegion.getWidth() > 0 && dirtyRegion.getHeight() > 0) { 310 // Set the clip rectangle using integer bounds since a fractional bounding box will 311 // still require a complete repaint on pixel boundaries 312 dirtyRect.setBounds(dirtyRegion); 313 // TODO I don't understand why this is needed. And if it is, are fractional pixelScale 314 // values OK? And if not, shouldn't pixelScale be an int instead? 315 if (pixelScale != 1.0f) { 316 dirtyRect.x *= pixelScale; 317 dirtyRect.y *= pixelScale; 318 dirtyRect.width *= pixelScale; 319 dirtyRect.height *= pixelScale; 320 } 321 g.setClipRect(dirtyRect); 322 g.setClipRectIndex(i); 323 doPaint(g, getRootPath(i)); 324 } 325 } 326 } else { 327 // There are no dirty regions, so just paint everything 328 g.setHasPreCullingBits(false); 329 g.setClipRect(null); 330 this.doPaint(g, null); 331 } 332 root.renderForcedContent(g); 333 334 // If we have an overlay then we need to render it too. 335 if (overlayRoot != null) { 336 overlayRoot.render(g); 337 } 338 339 // If we're showing dirty regions or overdraw, then we're going to need to draw 420 protected void disposePresentable() { 421 if (presentable instanceof GraphicsResource) { 422 ((GraphicsResource)presentable).dispose(); 423 } 424 presentable = null; 425 } 426 427 protected boolean validateStageGraphics() { 428 if (!sceneState.isValid()) { 429 // indicates something happened between the scheduling of the 430 // job and the running of this job. 431 return false; 432 } 433 434 width = viewWidth = sceneState.getWidth(); 435 height = viewHeight = sceneState.getHeight(); 436 437 return sceneState.isWindowVisible() && !sceneState.isWindowMinimized(); 438 } 439 440 protected float getPixelScaleFactor() { 441 return presentable == null ? 1.0f : presentable.getPixelScaleFactor(); 442 } 443 444 private void doPaint(Graphics g, NodePath renderRootPath) { 445 // Null path indicates that occlusion culling is not used 446 if (renderRootPath != null) { 447 if (renderRootPath.isEmpty()) { 448 // empty render path indicates that no rendering is needed. 449 // There may be occluded dirty Nodes however, so we need to clear them 450 root.clearDirtyTree(); 451 return; 452 } 453 // If the path is not empty, the first node must be the root node 454 assert(renderRootPath.getCurrentNode() == root); 455 } 456 if (PULSE_LOGGING_ENABLED) { 457 PulseLogger.newPhase("Painting"); 458 } 459 GlassScene scene = sceneState.getScene(); 460 scene.clearEntireSceneDirty(); 461 g.setLights(scene.getLights()); | 157 scaleTx.setToScale(width / 2.0, -height / 2.0, 1); 158 scaleTx.translate(1, -1); 159 projTx.mul(scaleTx); 160 viewProjTx = camera.getProjViewTx(viewProjTx); 161 projTx.mul(viewProjTx); 162 } 163 } 164 165 protected void paintImpl(final Graphics backBufferGraphics) { 166 // We should not be painting anything with a width / height 167 // that is <= 0, so we might as well bail right off. 168 if (width <= 0 || height <= 0 || backBufferGraphics == null) { 169 root.renderForcedContent(backBufferGraphics); 170 return; 171 } 172 173 // This "g" variable might represent the back buffer graphics, or it 174 // might be reassigned to the sceneBuffer graphics. 175 Graphics g = backBufferGraphics; 176 // Take into account the pixel scale factor for retina displays 177 final float pixelScaleX = getPixelScaleFactorX(); 178 final float pixelScaleY = getPixelScaleFactorY(); 179 // Cache pixelScale in Graphics for use in 3D shaders such as camera and light positions. 180 g.setPixelScaleFactors(pixelScaleX, pixelScaleY); 181 182 // Initialize renderEverything based on various conditions that will cause us to render 183 // the entire scene every time. 184 boolean renderEverything = overlayRoot != null || 185 freshBackBuffer || 186 sceneState.getScene().isEntireSceneDirty() || 187 sceneState.getScene().getDepthBuffer() || 188 !PrismSettings.dirtyOptsEnabled; 189 // We are going to draw dirty opt boxes either if we're supposed to show the dirty 190 // regions, or if we're supposed to show the overdraw boxes. 191 final boolean showDirtyOpts = PrismSettings.showDirtyRegions || PrismSettings.showOverdraw; 192 // If showDirtyOpts is turned on and we're not using a depth buffer 193 // then we will render the scene to an intermediate texture, and then at the end we'll 194 // draw that intermediate texture to the back buffer. 195 if (showDirtyOpts && !sceneState.getScene().getDepthBuffer()) { 196 final int bufferWidth = (int) Math.ceil(width * pixelScaleX); 197 final int bufferHeight = (int) Math.ceil(height * pixelScaleY); 198 // Check whether the sceneBuffer texture needs to be reconstructed 199 if (sceneBuffer != null) { 200 sceneBuffer.lock(); 201 if (sceneBuffer.isSurfaceLost() || 202 bufferWidth != sceneBuffer.getContentWidth() || 203 bufferHeight != sceneBuffer.getContentHeight()) { 204 sceneBuffer.unlock(); 205 sceneBuffer.dispose(); 206 sceneBuffer = null; 207 } 208 } 209 // If sceneBuffer is null, we need to create a new texture. In this 210 // case we will also need to render the whole scene (so don't bother 211 // with dirty opts) 212 if (sceneBuffer == null) { 213 sceneBuffer = g.getResourceFactory().createRTTexture( 214 bufferWidth, 215 bufferHeight, 216 Texture.WrapMode.CLAMP_TO_ZERO, 217 false); 218 renderEverything = true; 219 } 220 sceneBuffer.contentsUseful(); 221 // Hijack the "g" graphics variable 222 g = sceneBuffer.createGraphics(); 223 g.setPixelScaleFactors(pixelScaleX, pixelScaleY); 224 g.scale(pixelScaleX, pixelScaleY); 225 } else if (sceneBuffer != null) { 226 // We're in a situation where we have previously rendered to the sceneBuffer, but in 227 // this render pass for whatever reason we're going to draw directly to the back buffer. 228 // In this case we need to release the sceneBuffer. 229 sceneBuffer.dispose(); 230 sceneBuffer = null; 231 } 232 233 // The status will be set only if we're rendering with dirty regions 234 int status = -1; 235 236 // If we're rendering with dirty regions, then we'll call the root node to accumulate 237 // the dirty regions and then again to do the pre culling. 238 if (!renderEverything) { 239 if (PULSE_LOGGING_ENABLED) { 240 PulseLogger.newPhase("Dirty Opts Computed"); 241 } 242 clip.setBounds(0, 0, width, height); 243 dirtyRegionTemp.makeEmpty(); 244 dirtyRegionContainer.reset(); 297 if (!nodePath.isEmpty()) { 298 roots.add(nodePath.last()); 299 } 300 } 301 } 302 root.printDirtyOpts(s, roots); 303 PulseLogger.addMessage(s.toString()); 304 } 305 306 // Paint each dirty region 307 for (int i = 0; i < dirtyRegionSize; ++i) { 308 final RectBounds dirtyRegion = dirtyRegionContainer.getDirtyRegion(i); 309 // TODO it should be impossible to have ever created a dirty region that was empty... 310 // Make sure we are not trying to render in some invalid region 311 if (dirtyRegion.getWidth() > 0 && dirtyRegion.getHeight() > 0) { 312 // Set the clip rectangle using integer bounds since a fractional bounding box will 313 // still require a complete repaint on pixel boundaries 314 dirtyRect.setBounds(dirtyRegion); 315 // TODO I don't understand why this is needed. And if it is, are fractional pixelScale 316 // values OK? And if not, shouldn't pixelScale be an int instead? 317 if (pixelScaleX != 1.0f || pixelScaleY != 1.0f) { 318 dirtyRect.x *= pixelScaleX; 319 dirtyRect.y *= pixelScaleY; 320 dirtyRect.width *= pixelScaleX; 321 dirtyRect.height *= pixelScaleY; 322 } 323 g.setClipRect(dirtyRect); 324 g.setClipRectIndex(i); 325 doPaint(g, getRootPath(i)); 326 } 327 } 328 } else { 329 // There are no dirty regions, so just paint everything 330 g.setHasPreCullingBits(false); 331 g.setClipRect(null); 332 this.doPaint(g, null); 333 } 334 root.renderForcedContent(g); 335 336 // If we have an overlay then we need to render it too. 337 if (overlayRoot != null) { 338 overlayRoot.render(g); 339 } 340 341 // If we're showing dirty regions or overdraw, then we're going to need to draw 422 protected void disposePresentable() { 423 if (presentable instanceof GraphicsResource) { 424 ((GraphicsResource)presentable).dispose(); 425 } 426 presentable = null; 427 } 428 429 protected boolean validateStageGraphics() { 430 if (!sceneState.isValid()) { 431 // indicates something happened between the scheduling of the 432 // job and the running of this job. 433 return false; 434 } 435 436 width = viewWidth = sceneState.getWidth(); 437 height = viewHeight = sceneState.getHeight(); 438 439 return sceneState.isWindowVisible() && !sceneState.isWindowMinimized(); 440 } 441 442 protected float getPixelScaleFactorX() { 443 return presentable == null ? 1.0f : presentable.getPixelScaleFactorX(); 444 } 445 446 protected float getPixelScaleFactorY() { 447 return presentable == null ? 1.0f : presentable.getPixelScaleFactorY(); 448 } 449 450 private void doPaint(Graphics g, NodePath renderRootPath) { 451 // Null path indicates that occlusion culling is not used 452 if (renderRootPath != null) { 453 if (renderRootPath.isEmpty()) { 454 // empty render path indicates that no rendering is needed. 455 // There may be occluded dirty Nodes however, so we need to clear them 456 root.clearDirtyTree(); 457 return; 458 } 459 // If the path is not empty, the first node must be the root node 460 assert(renderRootPath.getCurrentNode() == root); 461 } 462 if (PULSE_LOGGING_ENABLED) { 463 PulseLogger.newPhase("Painting"); 464 } 465 GlassScene scene = sceneState.getScene(); 466 scene.clearEntireSceneDirty(); 467 g.setLights(scene.getLights()); |