126 lines
		
	
	
	
		
			4.7 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			126 lines
		
	
	
	
		
			4.7 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package com.mojang.blaze3d;
 | |
| 
 | |
| import com.mojang.blaze3d.buffers.GpuBuffer;
 | |
| import com.mojang.blaze3d.pipeline.RenderTarget;
 | |
| import com.mojang.blaze3d.systems.CommandEncoder;
 | |
| import com.mojang.blaze3d.systems.GpuDevice;
 | |
| import com.mojang.blaze3d.systems.RenderPass;
 | |
| import com.mojang.blaze3d.systems.RenderSystem;
 | |
| import com.mojang.blaze3d.textures.GpuTexture;
 | |
| import com.mojang.blaze3d.textures.GpuTextureView;
 | |
| import com.mojang.blaze3d.textures.TextureFormat;
 | |
| import com.mojang.blaze3d.vertex.VertexFormat;
 | |
| import com.mojang.jtracy.TracyClient;
 | |
| import java.util.OptionalInt;
 | |
| import net.fabricmc.api.EnvType;
 | |
| import net.fabricmc.api.Environment;
 | |
| import net.minecraft.client.renderer.RenderPipelines;
 | |
| 
 | |
| @Environment(EnvType.CLIENT)
 | |
| public class TracyFrameCapture implements AutoCloseable {
 | |
| 	private static final int MAX_WIDTH = 320;
 | |
| 	private static final int MAX_HEIGHT = 180;
 | |
| 	private static final int BYTES_PER_PIXEL = 4;
 | |
| 	private int targetWidth;
 | |
| 	private int targetHeight;
 | |
| 	private int width;
 | |
| 	private int height;
 | |
| 	private GpuTexture frameBuffer;
 | |
| 	private GpuTextureView frameBufferView;
 | |
| 	private GpuBuffer pixelbuffer;
 | |
| 	private int lastCaptureDelay;
 | |
| 	private boolean capturedThisFrame;
 | |
| 	private TracyFrameCapture.Status status = TracyFrameCapture.Status.WAITING_FOR_CAPTURE;
 | |
| 
 | |
| 	public TracyFrameCapture() {
 | |
| 		this.width = 320;
 | |
| 		this.height = 180;
 | |
| 		GpuDevice gpuDevice = RenderSystem.getDevice();
 | |
| 		this.frameBuffer = gpuDevice.createTexture("Tracy Frame Capture", 10, TextureFormat.RGBA8, this.width, this.height, 1, 1);
 | |
| 		this.frameBufferView = gpuDevice.createTextureView(this.frameBuffer);
 | |
| 		this.pixelbuffer = gpuDevice.createBuffer(() -> "Tracy Frame Capture buffer", 9, this.width * this.height * 4);
 | |
| 	}
 | |
| 
 | |
| 	private void resize(int width, int height) {
 | |
| 		float f = (float)width / height;
 | |
| 		if (width > 320) {
 | |
| 			width = 320;
 | |
| 			height = (int)(320.0F / f);
 | |
| 		}
 | |
| 
 | |
| 		if (height > 180) {
 | |
| 			width = (int)(180.0F * f);
 | |
| 			height = 180;
 | |
| 		}
 | |
| 
 | |
| 		width = width / 4 * 4;
 | |
| 		height = height / 4 * 4;
 | |
| 		if (this.width != width || this.height != height) {
 | |
| 			this.width = width;
 | |
| 			this.height = height;
 | |
| 			GpuDevice gpuDevice = RenderSystem.getDevice();
 | |
| 			this.frameBuffer.close();
 | |
| 			this.frameBuffer = gpuDevice.createTexture("Tracy Frame Capture", 10, TextureFormat.RGBA8, width, height, 1, 1);
 | |
| 			this.frameBufferView.close();
 | |
| 			this.frameBufferView = gpuDevice.createTextureView(this.frameBuffer);
 | |
| 			this.pixelbuffer.close();
 | |
| 			this.pixelbuffer = gpuDevice.createBuffer(() -> "Tracy Frame Capture buffer", 9, width * height * 4);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void capture(RenderTarget renderTarget) {
 | |
| 		if (this.status == TracyFrameCapture.Status.WAITING_FOR_CAPTURE && !this.capturedThisFrame && renderTarget.getColorTexture() != null) {
 | |
| 			this.capturedThisFrame = true;
 | |
| 			if (renderTarget.width != this.targetWidth || renderTarget.height != this.targetHeight) {
 | |
| 				this.targetWidth = renderTarget.width;
 | |
| 				this.targetHeight = renderTarget.height;
 | |
| 				this.resize(this.targetWidth, this.targetHeight);
 | |
| 			}
 | |
| 
 | |
| 			this.status = TracyFrameCapture.Status.WAITING_FOR_COPY;
 | |
| 			CommandEncoder commandEncoder = RenderSystem.getDevice().createCommandEncoder();
 | |
| 			RenderSystem.AutoStorageIndexBuffer autoStorageIndexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS);
 | |
| 			GpuBuffer gpuBuffer = autoStorageIndexBuffer.getBuffer(6);
 | |
| 
 | |
| 			try (RenderPass renderPass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(() -> "Tracy blit", this.frameBufferView, OptionalInt.empty())) {
 | |
| 				renderPass.setPipeline(RenderPipelines.TRACY_BLIT);
 | |
| 				renderPass.setVertexBuffer(0, RenderSystem.getQuadVertexBuffer());
 | |
| 				renderPass.setIndexBuffer(gpuBuffer, autoStorageIndexBuffer.type());
 | |
| 				renderPass.bindSampler("InSampler", renderTarget.getColorTextureView());
 | |
| 				renderPass.drawIndexed(0, 0, 6, 1);
 | |
| 			}
 | |
| 
 | |
| 			commandEncoder.copyTextureToBuffer(this.frameBuffer, this.pixelbuffer, 0, () -> this.status = TracyFrameCapture.Status.WAITING_FOR_UPLOAD, 0);
 | |
| 			this.lastCaptureDelay = 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void upload() {
 | |
| 		if (this.status == TracyFrameCapture.Status.WAITING_FOR_UPLOAD) {
 | |
| 			this.status = TracyFrameCapture.Status.WAITING_FOR_CAPTURE;
 | |
| 
 | |
| 			try (GpuBuffer.MappedView mappedView = RenderSystem.getDevice().createCommandEncoder().mapBuffer(this.pixelbuffer, true, false)) {
 | |
| 				TracyClient.frameImage(mappedView.data(), this.width, this.height, this.lastCaptureDelay, true);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void endFrame() {
 | |
| 		this.lastCaptureDelay++;
 | |
| 		this.capturedThisFrame = false;
 | |
| 		TracyClient.markFrame();
 | |
| 	}
 | |
| 
 | |
| 	public void close() {
 | |
| 		this.frameBuffer.close();
 | |
| 		this.frameBufferView.close();
 | |
| 		this.pixelbuffer.close();
 | |
| 	}
 | |
| 
 | |
| 	@Environment(EnvType.CLIENT)
 | |
| 	static enum Status {
 | |
| 		WAITING_FOR_CAPTURE,
 | |
| 		WAITING_FOR_COPY,
 | |
| 		WAITING_FOR_UPLOAD;
 | |
| 	}
 | |
| }
 |