package com.mojang.blaze3d.opengl; import com.mojang.blaze3d.IndexType; import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.buffers.GpuBufferSlice; import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.systems.GpuQueryPool; import com.mojang.blaze3d.systems.RenderPass; import com.mojang.blaze3d.systems.RenderPassBackend; import com.mojang.blaze3d.systems.ScissorState; import com.mojang.blaze3d.textures.GpuSampler; import com.mojang.blaze3d.textures.GpuTextureView; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import java.util.function.Supplier; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.SharedConstants; import org.jspecify.annotations.Nullable; @Environment(EnvType.CLIENT) class GlRenderPass implements RenderPassBackend { public static final boolean VALIDATION = SharedConstants.IS_RUNNING_IN_IDE; private final GlCommandEncoder encoder; private final GlDevice device; private final boolean hasDepthTexture; private final ScissorState defaultScissorState; @Nullable protected GlRenderPipeline pipeline; protected final GpuBufferSlice[] vertexBuffers = new GpuBufferSlice[16]; protected boolean vertexBufferDirty = true; @Nullable protected GpuBuffer indexBuffer; protected IndexType indexType = IndexType.INT; private final ScissorState scissorState = new ScissorState(); protected final HashMap uniforms = new HashMap(); protected final HashMap samplers = new HashMap(); protected final Set dirtyUniforms = new HashSet(); protected final int colorAttachmentCount; public GlRenderPass( final GlCommandEncoder encoder, final GlDevice device, final boolean hasDepthTexture, final int colorAttachmentCount, final ScissorState defaultScissorState ) { this.encoder = encoder; this.device = device; this.hasDepthTexture = hasDepthTexture; this.colorAttachmentCount = colorAttachmentCount; this.defaultScissorState = defaultScissorState; this.scissorState.setFrom(defaultScissorState); } public boolean hasDepthTexture() { return this.hasDepthTexture; } @Override public void pushDebugGroup(final Supplier label) { this.device.debugLabels().pushDebugGroup(label); } @Override public void popDebugGroup() { this.device.debugLabels().popDebugGroup(); } @Override public void setPipeline(final RenderPipeline pipeline) { if (this.pipeline == null || this.pipeline.info() != pipeline) { this.dirtyUniforms.addAll(this.uniforms.keySet()); this.dirtyUniforms.addAll(this.samplers.keySet()); } this.pipeline = this.device.getOrCompilePipeline(pipeline); } @Override public void bindTexture(final String name, @Nullable final GpuTextureView textureView, @Nullable final GpuSampler sampler) { if (sampler == null) { this.samplers.remove(name); } else { this.samplers.put(name, new GlRenderPass.TextureViewAndSampler((GlTextureView)textureView, (GlSampler)sampler)); } this.dirtyUniforms.add(name); } @Override public void setUniform(final String name, final GpuBuffer value) { this.uniforms.put(name, value.slice()); this.dirtyUniforms.add(name); } @Override public void setUniform(final String name, final GpuBufferSlice value) { this.uniforms.put(name, value); this.dirtyUniforms.add(name); } @Override public void enableScissor(final int x, final int y, final int width, final int height) { this.scissorState.enable(x, y, width, height); } @Override public void disableScissor() { this.scissorState.setFrom(this.defaultScissorState); } public boolean isScissorEnabled() { return this.scissorState.enabled(); } public int getScissorX() { return this.scissorState.x(); } public int getScissorY() { return this.scissorState.y(); } public int getScissorWidth() { return this.scissorState.width(); } public int getScissorHeight() { return this.scissorState.height(); } @Override public void setVertexBuffer(final int slot, @Nullable final GpuBufferSlice vertexBuffer) { GpuBuffer inputBuffer = vertexBuffer != null ? vertexBuffer.buffer() : null; GpuBuffer existingBuffer = this.vertexBuffers[slot] != null ? this.vertexBuffers[slot].buffer() : null; long inputOffset = vertexBuffer != null ? vertexBuffer.offset() : 0L; long exitingOffset = this.vertexBuffers[slot] != null ? this.vertexBuffers[slot].offset() : 0L; this.vertexBufferDirty |= inputBuffer != existingBuffer || inputOffset != exitingOffset; this.vertexBuffers[slot] = vertexBuffer; } @Override public void setIndexBuffer(@Nullable final GpuBuffer indexBuffer, final IndexType indexType) { this.indexBuffer = indexBuffer; this.indexType = indexType; } @Override public void drawIndexed(final int baseVertex, final int firstIndex, final int indexCount, final int instanceCount) { this.encoder.executeDraw(this, baseVertex, firstIndex, indexCount, this.indexType, instanceCount); } @Override public void drawMultipleIndexed( final Collection> draws, @Nullable final GpuBuffer defaultIndexBuffer, @Nullable final IndexType defaultIndexType, final Collection dynamicUniforms, final T uniformArgument ) { this.encoder.executeDrawMultiple(this, draws, defaultIndexBuffer, defaultIndexType, dynamicUniforms, uniformArgument); } @Override public void draw(final int firstVertex, final int vertexCount) { this.encoder.executeDraw(this, firstVertex, 0, vertexCount, null, 1); } @Override public void writeTimestamp(final GpuQueryPool pool, final int index) { ((GlQueryPool)pool).writeTimestamp(index); } @Environment(EnvType.CLIENT) protected record TextureViewAndSampler(GlTextureView view, GlSampler sampler) { } }