private void downsampleSceneInto1x1pixelsBuffer() { PerformanceMonitor.startActivity("Rendering eye adaption"); materials.downSampler.enable(); FBO downSampledFBO; for (int i = 4; i >= 0; i--) { downSampledFBO = buffers.downSampledScene[i]; materials.downSampler.setFloat("size", downSampledFBO.width(), true); downSampledFBO.bind(); setViewportTo(downSampledFBO.dimensions()); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // TODO: move this block above, for consistency if (i == 4) { buffers.initialPost.bindTexture(); } else { buffers.downSampledScene[i + 1].bindTexture(); } renderFullscreenQuad(); graphicState.bindDisplay(); // TODO: probably can be removed or moved out of the loop } setViewportToWholeDisplay(); // TODO: verify this is necessary PerformanceMonitor.endActivity(); }
private void checkForUnload() { PerformanceMonitor.startActivity("Unloading irrelevant chunks"); int unloaded = 0; logger.debug("Compacting cache"); Iterator<Vector3i> iterator = nearCache.keySet().iterator(); while (iterator.hasNext()) { Vector3i pos = iterator.next(); boolean keep = false; for (ChunkRelevanceRegion region : regions.values()) { if (region.getCurrentRegion().expand(UNLOAD_LEEWAY).encompasses(pos)) { keep = true; break; } } if (!keep) { // TODO: need some way to not dispose chunks being edited or processed (or do so safely) // Note: Above won't matter if all changes are on the main thread if (unloadChunkInternal(pos)) { iterator.remove(); if (++unloaded >= UNLOAD_PER_FRAME) { break; } } } } PerformanceMonitor.endActivity(); }
/** * If blur is enabled through the rendering settings, this method generates the images used by the * Blur effect when underwater and for the Depth of Field effect when above water. * * <p>For more information on blur: http://en.wikipedia.org/wiki/Defocus_aberration For more * information on DoF: http://en.wikipedia.org/wiki/Depth_of_field */ public void generateBlurPasses() { if (renderingConfig.getBlurIntensity() != 0) { PerformanceMonitor.startActivity("Generating Blur Passes"); generateBlur(buffers.sceneBlur0); generateBlur(buffers.sceneBlur1); PerformanceMonitor.endActivity(); } }
// TODO: verify if this can be achieved entirely in the GPU, during tone mapping perhaps? public void downsampleSceneAndUpdateExposure() { if (renderingConfig.isEyeAdaptation()) { PerformanceMonitor.startActivity("Updating exposure"); downsampleSceneInto1x1pixelsBuffer(); renderingProcess .getCurrentReadbackPBO() .copyFromFBO( buffers.downSampledScene[0].fboId, 1, 1, GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE); renderingProcess.swapReadbackPBOs(); ByteBuffer pixels = renderingProcess.getCurrentReadbackPBO().readBackPixels(); if (pixels.limit() < 3) { logger.error("Failed to auto-update the exposure value."); return; } // TODO: make this line more readable by breaking it in smaller pieces currentSceneLuminance = 0.2126f * (pixels.get(2) & 0xFF) / 255.f + 0.7152f * (pixels.get(1) & 0xFF) / 255.f + 0.0722f * (pixels.get(0) & 0xFF) / 255.f; float targetExposure = hdrMaxExposure; if (currentSceneLuminance > 0) { targetExposure = hdrTargetLuminance / currentSceneLuminance; } float maxExposure = hdrMaxExposure; if (CoreRegistry.get(BackdropProvider.class).getDaylight() == 0.0) { // TODO: fetch the backdropProvider earlier and only once maxExposure = hdrMaxExposureNight; } if (targetExposure > maxExposure) { targetExposure = maxExposure; } else if (targetExposure < hdrMinExposure) { targetExposure = hdrMinExposure; } currentExposure = TeraMath.lerp(currentExposure, targetExposure, hdrExposureAdjustmentSpeed); } else { if (CoreRegistry.get(BackdropProvider.class).getDaylight() == 0.0) { currentExposure = hdrMaxExposureNight; } else { currentExposure = hdrExposureDefault; } } PerformanceMonitor.endActivity(); }
@Override public void update(float delta) { PerformanceMonitor.startActivity("Temp Blocks Cleanup"); List<EntityRef> toRemove = Lists.newArrayList(temporaryBlockEntities); temporaryBlockEntities.clear(); for (EntityRef entity : toRemove) { cleanUpTemporaryEntity(entity); } PerformanceMonitor.endActivity(); }
/** * If bloom is enabled via the rendering settings, this method generates the images needed for the * bloom shader effect and stores them in their own frame buffers. * * <p>This effects renders adds fringes (or "feathers") of light to areas of intense brightness. * This in turn give the impression of those areas partially overwhelming the camera or the eye. * * <p>For more information see: http://en.wikipedia.org/wiki/Bloom_(shader_effect) */ public void generateBloomPasses() { if (renderingConfig.isBloom()) { PerformanceMonitor.startActivity("Generating Bloom Passes"); generateHighPass(); generateBloom(buffers.sceneBloom0); generateBloom(buffers.sceneBloom1); generateBloom(buffers.sceneBloom2); PerformanceMonitor.endActivity(); } }
/** * If each is enabled through the rendering settings, this method adds depth-of-field blur, motion * blur and film grain to the rendering obtained so far. If OculusVR support is enabled, it * composes (over two calls) the images for each eye into a single image, and applies a distortion * pattern to each, to match the optics in the OculusVR headset. * * <p>Finally, it either sends the image to the display or, when taking a screenshot, instructs * the rendering process to save it to a file. // TODO: update this sentence when the * FrameBuffersManager becomes available. * * @param renderingStage Can be MONO, LEFT_EYE or RIGHT_EYE, and communicates to the method * weather it is dealing with a standard display or an OculusVR setup, and in the latter case, * which eye is currently being rendered. Notice that if the OculusVR support is enabled, the * image is sent to screen or saved to file only when the value passed in is RIGHT_EYE, as the * processing for the LEFT_EYE comes first and leads to an incomplete image. */ public void finalPostProcessing(WorldRenderer.WorldRenderingStage renderingStage) { PerformanceMonitor.startActivity("Rendering final scene"); if (!renderingDebugConfig.isEnabled()) { materials.finalPost.enable(); } else { materials.debug.enable(); } if (!renderingConfig.isOculusVrSupport()) { renderFinalMonoImage(); } else { renderFinalStereoImage(renderingStage); } PerformanceMonitor.endActivity(); }
// TODO: Tone mapping usually maps colors from HDR to a more limited range, // TODO: i.e. the 24 bit a monitor can display. This method however maps from an HDR buffer // TODO: to another HDR buffer and this puzzles me. Will need to dig deep in the shader to // TODO: see what it does. public void generateToneMappedScene() { PerformanceMonitor.startActivity("Tone mapping"); materials.toneMapping.enable(); buffers.sceneToneMapped.bind(); setViewportTo(buffers.sceneToneMapped.dimensions()); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // TODO: verify this is necessary renderFullscreenQuad(); graphicState.bindDisplay(); // TODO: verify this is necessary setViewportToWholeDisplay(); // TODO: verify this is necessary PerformanceMonitor.endActivity(); }
/** * Adds chromatic aberration, light shafts, 1/8th resolution bloom, vignette onto the rendering * achieved so far. Stores the result into its own buffer to be used at a later stage. */ public void initialPostProcessing() { PerformanceMonitor.startActivity("Initial Post-Processing"); materials.initialPost.enable(); // TODO: verify what the inputs are buffers.initialPost.bind(); // TODO: see if we could write this straight into sceneOpaque setViewportTo(buffers.initialPost.dimensions()); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // TODO: verify this is necessary renderFullscreenQuad(); graphicState.bindDisplay(); // TODO: verify this is necessary setViewportToWholeDisplay(); // TODO: verify this is necessary PerformanceMonitor.endActivity(); }
/** Generates light shafts and stores them in their own FBO. */ public void generateLightShafts() { if (renderingConfig.isLightShafts()) { PerformanceMonitor.startActivity("Rendering light shafts"); materials.lightShafts.enable(); // TODO: verify what the inputs are buffers.lightShafts.bind(); setViewportTo(buffers.lightShafts.dimensions()); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // TODO: verify this is necessary renderFullscreenQuad(); graphicState.bindDisplay(); // TODO: verify this is necessary setViewportToWholeDisplay(); // TODO: verify this is necessary PerformanceMonitor.endActivity(); } }
private void startSaving() { logger.info("Saving - Creating game snapshot"); PerformanceMonitor.startActivity("Auto Saving"); ComponentSystemManager componentSystemManager = CoreRegistry.get(ComponentSystemManager.class); for (ComponentSystem sys : componentSystemManager.iterateAll()) { sys.preSave(); } saveRequested = false; saveTransaction = createSaveTransaction(); saveThreadManager.offer(saveTransaction); for (ComponentSystem sys : componentSystemManager.iterateAll()) { sys.postSave(); } scheduleNextAutoSave(); PerformanceMonitor.endActivity(); entitySetDeltaRecorder = new EntitySetDeltaRecorder(this.entityRefReplacingComponentLibrary); logger.info("Saving - Snapshot created: Writing phase starts"); }
private void makeChunksAvailable() { List<ReadyChunkInfo> newReadyChunks = Lists.newArrayListWithExpectedSize(readyChunks.size()); readyChunks.drainTo(newReadyChunks); for (ReadyChunkInfo readyChunkInfo : newReadyChunks) { nearCache.put(readyChunkInfo.getPos(), readyChunkInfo.getChunk()); preparingChunks.remove(readyChunkInfo.getPos()); } if (!newReadyChunks.isEmpty()) { sortedReadyChunks.addAll(newReadyChunks); Collections.sort(sortedReadyChunks, new ReadyChunkRelevanceComparator()); } if (!sortedReadyChunks.isEmpty()) { boolean loaded = false; for (int i = sortedReadyChunks.size() - 1; i >= 0 && !loaded; i--) { ReadyChunkInfo chunkInfo = sortedReadyChunks.get(i); PerformanceMonitor.startActivity("Make Chunk Available"); if (makeChunkAvailable(chunkInfo)) { sortedReadyChunks.remove(i); loaded = true; } PerformanceMonitor.endActivity(); } } }
/** * The main loop runs until the EngineState is set back to INITIALIZED by shutdown() or until the * OS requests the application's window to be closed. Engine cleanup and disposal occur * afterwards. */ private void mainLoop() { PerformanceMonitor.startActivity("Other"); // MAIN GAME LOOP while (!shutdownRequested) { assetTypeManager.reloadChangedOnDisk(); processPendingState(); if (currentState == null) { shutdown(); break; } Iterator<Float> updateCycles = timeSubsystem.getEngineTime().tick(); for (EngineSubsystem subsystem : allSubsystems) { try (Activity ignored = PerformanceMonitor.startActivity(subsystem.getName() + " PreUpdate")) { subsystem.preUpdate(currentState, timeSubsystem.getEngineTime().getRealDelta()); } } while (updateCycles.hasNext()) { float updateDelta = updateCycles.next(); // gameTime gets updated here! try (Activity ignored = PerformanceMonitor.startActivity("Main Update")) { currentState.update(updateDelta); } } // Waiting processes are set by modules via GameThread.a/synch() methods. GameThread.processWaitingProcesses(); for (EngineSubsystem subsystem : getSubsystems()) { try (Activity ignored = PerformanceMonitor.startActivity(subsystem.getName() + " Subsystem postUpdate")) { subsystem.postUpdate(currentState, timeSubsystem.getEngineTime().getRealDelta()); } } assetTypeManager.disposedUnusedAssets(); PerformanceMonitor.rollCycle(); PerformanceMonitor.startActivity("Other"); } PerformanceMonitor.endActivity(); }
@Override public void completeUpdate() { ReadyChunkInfo readyChunkInfo = lightMerger.completeMerge(); if (readyChunkInfo != null) { Chunk chunk = readyChunkInfo.getChunk(); chunk.lock(); try { chunk.markReady(); updateAdjacentChunksReadyFieldOf(chunk); updateAdjacentChunksReadyFieldOfAdjChunks(chunk); if (!readyChunkInfo.isNewChunk()) { PerformanceMonitor.startActivity("Generating Block Entities"); generateBlockEntities(chunk); PerformanceMonitor.endActivity(); } if (readyChunkInfo.getChunkStore() != null) { readyChunkInfo.getChunkStore().restoreEntities(); } if (!readyChunkInfo.isNewChunk()) { PerformanceMonitor.startActivity("Sending OnAddedBlocks"); readyChunkInfo .getBlockPositionMapppings() .forEachEntry( new TShortObjectProcedure<TIntList>() { @Override public boolean execute(short id, TIntList positions) { if (positions.size() > 0) { blockManager .getBlock(id) .getEntity() .send(new OnAddedBlocks(positions, registry)); } return true; } }); PerformanceMonitor.endActivity(); } PerformanceMonitor.startActivity("Sending OnActivateBlocks"); readyChunkInfo .getBlockPositionMapppings() .forEachEntry( new TShortObjectProcedure<TIntList>() { @Override public boolean execute(short id, TIntList positions) { if (positions.size() > 0) { blockManager .getBlock(id) .getEntity() .send(new OnActivatedBlocks(positions, registry)); } return true; } }); PerformanceMonitor.endActivity(); if (!readyChunkInfo.isNewChunk()) { worldEntity.send(new OnChunkGenerated(readyChunkInfo.getPos())); } worldEntity.send(new OnChunkLoaded(readyChunkInfo.getPos())); for (ChunkRelevanceRegion region : regions.values()) { region.chunkReady(chunk); } } finally { chunk.unlock(); } } }
/** * The main loop runs until the EngineState is set back to INITIALIZED by shutdown() or until the * OS requests the application's window to be closed. Engine cleanup and disposal occur * afterwards. */ private void mainLoop() { NetworkSystem networkSystem = CoreRegistry.get(NetworkSystem.class); DisplayDevice display = CoreRegistry.get(DisplayDevice.class); PerformanceMonitor.startActivity("Other"); // MAIN GAME LOOP while (engineState == EngineState.RUNNING && !display.isCloseRequested()) { long totalDelta; float updateDelta; float subsystemsDelta; // Only process rendering and updating once a second if (!display.isActive() && isHibernationAllowed()) { time.setPaused(true); Iterator<Float> updateCycles = time.tick(); while (updateCycles.hasNext()) { updateCycles.next(); } try { Thread.sleep(100); } catch (InterruptedException e) { logger.warn("Display inactivity sleep interrupted", e); } display.processMessages(); time.setPaused(false); continue; } processPendingState(); if (currentState == null) { shutdown(); break; } Iterator<Float> updateCycles = time.tick(); try (Activity ignored = PerformanceMonitor.startActivity("Network Update")) { networkSystem.update(); } totalDelta = 0; while (updateCycles.hasNext()) { updateDelta = updateCycles.next(); // gameTime gets updated here! totalDelta += time.getDeltaInMs(); try (Activity ignored = PerformanceMonitor.startActivity("Main Update")) { currentState.update(updateDelta); } } subsystemsDelta = totalDelta / 1000f; for (EngineSubsystem subsystem : getSubsystems()) { try (Activity ignored = PerformanceMonitor.startActivity(subsystem.getClass().getSimpleName())) { subsystem.preUpdate(currentState, subsystemsDelta); } } // Waiting processes are set by modules via GameThread.a/synch() methods. GameThread.processWaitingProcesses(); for (EngineSubsystem subsystem : getSubsystems()) { try (Activity ignored = PerformanceMonitor.startActivity(subsystem.getClass().getSimpleName())) { subsystem.postUpdate(currentState, subsystemsDelta); } } PerformanceMonitor.rollCycle(); PerformanceMonitor.startActivity("Other"); } PerformanceMonitor.endActivity(); // This becomes important only if display.isCloseRequested() is true. // In all other circumstances the EngineState is already set to // INITIALIZED by the time the flow gets here. engineState = EngineState.INITIALIZED; }