package com.mojang.blaze3d.pipeline; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.TextureUtil; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.BufferUploader; import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.VertexFormat; import java.util.Objects; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.Util; import net.minecraft.client.renderer.CompiledShaderProgram; import net.minecraft.client.renderer.CoreShaders; @Environment(EnvType.CLIENT) public abstract class RenderTarget { private static final int RED_CHANNEL = 0; private static final int GREEN_CHANNEL = 1; private static final int BLUE_CHANNEL = 2; private static final int ALPHA_CHANNEL = 3; public int width; public int height; public int viewWidth; public int viewHeight; public final boolean useDepth; public int frameBufferId; protected int colorTextureId; protected int depthBufferId; private final float[] clearChannels = Util.make(() -> new float[]{1.0F, 1.0F, 1.0F, 0.0F}); public int filterMode; public RenderTarget(boolean useDepth) { this.useDepth = useDepth; this.frameBufferId = -1; this.colorTextureId = -1; this.depthBufferId = -1; } public void resize(int width, int height) { RenderSystem.assertOnRenderThreadOrInit(); GlStateManager._enableDepthTest(); if (this.frameBufferId >= 0) { this.destroyBuffers(); } this.createBuffers(width, height); GlStateManager._glBindFramebuffer(36160, 0); } public void destroyBuffers() { RenderSystem.assertOnRenderThreadOrInit(); this.unbindRead(); this.unbindWrite(); if (this.depthBufferId > -1) { TextureUtil.releaseTextureId(this.depthBufferId); this.depthBufferId = -1; } if (this.colorTextureId > -1) { TextureUtil.releaseTextureId(this.colorTextureId); this.colorTextureId = -1; } if (this.frameBufferId > -1) { GlStateManager._glBindFramebuffer(36160, 0); GlStateManager._glDeleteFramebuffers(this.frameBufferId); this.frameBufferId = -1; } } public void copyDepthFrom(RenderTarget otherTarget) { RenderSystem.assertOnRenderThreadOrInit(); GlStateManager._glBindFramebuffer(36008, otherTarget.frameBufferId); GlStateManager._glBindFramebuffer(36009, this.frameBufferId); GlStateManager._glBlitFrameBuffer(0, 0, otherTarget.width, otherTarget.height, 0, 0, this.width, this.height, 256, 9728); GlStateManager._glBindFramebuffer(36160, 0); } public void createBuffers(int width, int height) { RenderSystem.assertOnRenderThreadOrInit(); int i = RenderSystem.maxSupportedTextureSize(); if (width > 0 && width <= i && height > 0 && height <= i) { this.viewWidth = width; this.viewHeight = height; this.width = width; this.height = height; this.frameBufferId = GlStateManager.glGenFramebuffers(); this.colorTextureId = TextureUtil.generateTextureId(); if (this.useDepth) { this.depthBufferId = TextureUtil.generateTextureId(); GlStateManager._bindTexture(this.depthBufferId); GlStateManager._texParameter(3553, 10241, 9728); GlStateManager._texParameter(3553, 10240, 9728); GlStateManager._texParameter(3553, 34892, 0); GlStateManager._texParameter(3553, 10242, 33071); GlStateManager._texParameter(3553, 10243, 33071); GlStateManager._texImage2D(3553, 0, 6402, this.width, this.height, 0, 6402, 5126, null); } this.setFilterMode(9728, true); GlStateManager._bindTexture(this.colorTextureId); GlStateManager._texParameter(3553, 10242, 33071); GlStateManager._texParameter(3553, 10243, 33071); GlStateManager._texImage2D(3553, 0, 32856, this.width, this.height, 0, 6408, 5121, null); GlStateManager._glBindFramebuffer(36160, this.frameBufferId); GlStateManager._glFramebufferTexture2D(36160, 36064, 3553, this.colorTextureId, 0); if (this.useDepth) { GlStateManager._glFramebufferTexture2D(36160, 36096, 3553, this.depthBufferId, 0); } this.checkStatus(); this.clear(); this.unbindRead(); } else { throw new IllegalArgumentException("Window " + width + "x" + height + " size out of bounds (max. size: " + i + ")"); } } public void setFilterMode(int filterMode) { this.setFilterMode(filterMode, false); } private void setFilterMode(int filterMode, boolean force) { RenderSystem.assertOnRenderThreadOrInit(); if (force || filterMode != this.filterMode) { this.filterMode = filterMode; GlStateManager._bindTexture(this.colorTextureId); GlStateManager._texParameter(3553, 10241, filterMode); GlStateManager._texParameter(3553, 10240, filterMode); GlStateManager._bindTexture(0); } } public void checkStatus() { RenderSystem.assertOnRenderThreadOrInit(); int i = GlStateManager.glCheckFramebufferStatus(36160); if (i != 36053) { if (i == 36054) { throw new RuntimeException("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); } else if (i == 36055) { throw new RuntimeException("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"); } else if (i == 36059) { throw new RuntimeException("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"); } else if (i == 36060) { throw new RuntimeException("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"); } else if (i == 36061) { throw new RuntimeException("GL_FRAMEBUFFER_UNSUPPORTED"); } else if (i == 1285) { throw new RuntimeException("GL_OUT_OF_MEMORY"); } else { throw new RuntimeException("glCheckFramebufferStatus returned unknown status:" + i); } } } public void bindRead() { RenderSystem.assertOnRenderThread(); GlStateManager._bindTexture(this.colorTextureId); } public void unbindRead() { RenderSystem.assertOnRenderThreadOrInit(); GlStateManager._bindTexture(0); } public void bindWrite(boolean setViewport) { RenderSystem.assertOnRenderThreadOrInit(); GlStateManager._glBindFramebuffer(36160, this.frameBufferId); if (setViewport) { GlStateManager._viewport(0, 0, this.viewWidth, this.viewHeight); } } public void unbindWrite() { RenderSystem.assertOnRenderThreadOrInit(); GlStateManager._glBindFramebuffer(36160, 0); } public void setClearColor(float red, float green, float blue, float alpha) { this.clearChannels[0] = red; this.clearChannels[1] = green; this.clearChannels[2] = blue; this.clearChannels[3] = alpha; } public void blitToScreen(int width, int height) { GlStateManager._glBindFramebuffer(36008, this.frameBufferId); GlStateManager._glBlitFrameBuffer(0, 0, this.width, this.height, 0, 0, width, height, 16384, 9728); GlStateManager._glBindFramebuffer(36008, 0); } public void blitAndBlendToScreen(int width, int height) { RenderSystem.assertOnRenderThread(); GlStateManager._colorMask(true, true, true, false); GlStateManager._disableDepthTest(); GlStateManager._depthMask(false); GlStateManager._viewport(0, 0, width, height); CompiledShaderProgram compiledShaderProgram = (CompiledShaderProgram)Objects.requireNonNull( RenderSystem.setShader(CoreShaders.BLIT_SCREEN), "Blit shader not loaded" ); compiledShaderProgram.bindSampler("InSampler", this.colorTextureId); BufferBuilder bufferBuilder = RenderSystem.renderThreadTesselator().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLIT_SCREEN); bufferBuilder.addVertex(0.0F, 0.0F, 0.0F); bufferBuilder.addVertex(1.0F, 0.0F, 0.0F); bufferBuilder.addVertex(1.0F, 1.0F, 0.0F); bufferBuilder.addVertex(0.0F, 1.0F, 0.0F); BufferUploader.drawWithShader(bufferBuilder.buildOrThrow()); GlStateManager._depthMask(true); GlStateManager._colorMask(true, true, true, true); } public void clear() { RenderSystem.assertOnRenderThreadOrInit(); this.bindWrite(true); GlStateManager._clearColor(this.clearChannels[0], this.clearChannels[1], this.clearChannels[2], this.clearChannels[3]); int i = 16384; if (this.useDepth) { GlStateManager._clearDepth(1.0); i |= 256; } GlStateManager._clear(i); this.unbindWrite(); } public int getColorTextureId() { return this.colorTextureId; } public int getDepthTextureId() { return this.depthBufferId; } }