Skip to content

Commit e46ccaf

Browse files
committed
Destroy framebuffers upon image view destruction
1 parent 2579e76 commit e46ccaf

7 files changed

Lines changed: 216 additions & 181 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package git.artdeell.artvk;
2+
3+
public interface Dependent {
4+
void parentClosed();
5+
}

src/main/java/git/artdeell/artvk/Vk11CommandEncoder.java

Lines changed: 60 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -228,106 +228,74 @@ public void submit() {
228228
return this.transientMemory;
229229
}
230230

231-
@Override
232-
public @NotNull RenderPassBackend createRenderPass(final RenderPassDescriptor descriptor) {
233-
234-
List<RenderPassDescriptor.Attachment<@NotNull Optional<Vector4fc>>> colorAttachments = descriptor.colorAttachments();
235-
Vk11GpuTextureView[] colorTextures = new Vk11GpuTextureView[colorAttachments.size()];
236-
237-
MemoryStack transStack = MemoryStack.stackGet();
238-
for (int i = 0; i < colorAttachments.size(); i++) {
239-
RenderPassDescriptor.Attachment<@NotNull Optional<Vector4fc>> attachment = colorAttachments.get(i);
240-
if(attachment == null) {
241-
colorTextures[i] = null;
242-
continue;
231+
@Override
232+
public @NotNull RenderPassBackend createRenderPass(final RenderPassDescriptor descriptor) {
233+
try(MemoryStack memoryStack = MemoryStack.stackPush()) {
234+
List<RenderPassDescriptor.Attachment<@NotNull Optional<Vector4fc>>> colorAttachments = descriptor.colorAttachments();
235+
RenderPassDescriptor.Attachment<@NotNull OptionalDouble> depthAttachment = descriptor.depthAttachment();
236+
int colorCount = colorAttachments.size();
237+
238+
boolean hasDepth = depthAttachment != null;
239+
Vk11GpuTextureView[] attachmentViews = new Vk11GpuTextureView[colorCount + (hasDepth ? 1 : 0)];
240+
241+
VkClearValue.Buffer clearValues = VkClearValue.calloc(attachmentViews.length, memoryStack);
242+
int[] colorFormats = new int[colorCount];
243+
int depthFormat = VK10.VK_FORMAT_UNDEFINED;
244+
245+
for (int i = 0; i < colorCount; i++) {
246+
RenderPassDescriptor.Attachment<@NotNull Optional<Vector4fc>> attachment = colorAttachments.get(i);
247+
if(attachment == null) {
248+
attachmentViews[i] = null;
249+
colorFormats[i] = VK10.VK_FORMAT_UNDEFINED;
250+
continue;
251+
}
252+
Vk11GpuTextureView textureView = (Vk11GpuTextureView)attachment.textureView();
253+
textureView.disableTransferMode(memoryStack, currentCommandBuffer);
254+
attachmentViews[i] = textureView;
255+
colorFormats[i] = Vk11Const.toVk(textureView.texture().getFormat());
256+
if(attachment.clearValue().isPresent()) {
257+
Vk11Utils.putArgb(clearValues.get(i).color(), attachment.clearValue().get());
258+
}
259+
}
260+
if(hasDepth) {
261+
attachmentViews[colorCount] = (Vk11GpuTextureView) depthAttachment.textureView();
262+
depthFormat = Vk11Const.toVk(depthAttachment.textureView().texture().getFormat());
263+
if(depthAttachment.clearValue().isPresent()) {
264+
clearValues.get(colorCount).depthStencil().depth((float) depthAttachment.clearValue().getAsDouble());
265+
}
243266
}
244-
Vk11GpuTextureView textureView = (Vk11GpuTextureView)attachment.textureView();
245-
textureView.disableTransferMode(transStack, currentCommandBuffer);
246-
colorTextures[i] = textureView;
247-
}
248-
249-
RenderPassDescriptor.Attachment<@NotNull OptionalDouble> depthAttachment = descriptor.depthAttachment();
250-
this.device.instance().debug().beginDebugGroup(this.commandBuffer(), descriptor.label());
251-
252-
int width = 0;
253-
int height = 0;
254-
if (!colorAttachments.isEmpty()) {
255-
for (RenderPassDescriptor.Attachment<@NotNull Optional<Vector4fc>> colorAttachment : colorAttachments) {
256-
if (colorAttachment != null) {
257-
GpuTextureView colorTexture = colorAttachment.textureView();
258-
width = colorTexture.getWidth(0);
259-
height = colorTexture.getHeight(0);
260-
}
261-
}
262-
} else if (depthAttachment != null) {
263-
width = depthAttachment.textureView().getWidth(0);
264-
height = depthAttachment.textureView().getHeight(0);
265-
}
266-
267-
try (MemoryStack stack = MemoryStack.stackPush()) {
268-
// Build color formats list
269-
List<Integer> colorFormats = new java.util.ArrayList<>();
270-
for (Vk11GpuTextureView cv : colorTextures) {
271-
if (cv != null) {
272-
colorFormats.add(Vk11Const.toVk(cv.texture().getFormat()));
273-
} else {
274-
colorFormats.add(VK10.VK_FORMAT_UNDEFINED);
275-
}
276-
}
277267

278-
boolean hasDepth = depthAttachment != null;
279-
int depthFormat = hasDepth ? Vk11Const.toVk(depthAttachment.textureView().texture().getFormat()) : VK10.VK_FORMAT_UNDEFINED;
268+
this.device.instance().debug().beginDebugGroup(this.commandBuffer(), descriptor.label());
280269

281-
long renderPass = this.device.renderPassCache().getOrCreateRenderPass(colorFormats, hasDepth, depthFormat);
270+
int width = 0, height = 0;
271+
for(Vk11GpuTextureView view : attachmentViews) {
272+
if(view == null) continue;
273+
width = view.getWidth(0);
274+
height = view.getHeight(0);
275+
break;
276+
}
282277

283-
// Build image views array for framebuffer
284-
int viewCount = colorTextures.length + (hasDepth ? 1 : 0);
285-
long[] imageViews = new long[viewCount];
286-
for (int i = 0; i < colorTextures.length; i++) {
287-
imageViews[i] = colorTextures[i] != null ? colorTextures[i].vkImageView() : 0L;
288-
}
289-
if (hasDepth) {
290-
imageViews[colorTextures.length] = ((Vk11GpuTextureView)depthAttachment.textureView()).vkImageView();
291-
}
278+
long renderPass = this.device.renderPassCache().getOrCreateRenderPass(colorFormats, hasDepth, depthFormat);
292279

293-
long framebuffer = this.device.renderPassCache().getOrCreateFramebuffer(renderPass, width, height, imageViews);
294-
295-
// Build clear values
296-
org.lwjgl.vulkan.VkClearValue.Buffer clearValues = VkClearValue.calloc(viewCount, stack);
297-
for (int i = 0; i < colorTextures.length; i++) {
298-
if (colorTextures[i] != null) {
299-
RenderPassDescriptor.Attachment<@NotNull Optional<Vector4fc>> attachment = colorAttachments.get(i);
300-
if (attachment.clearValue().isPresent()) {
301-
Vector4fc color = attachment.clearValue().get();
302-
System.out.println("Clear value: "+color);
303-
Vk11Utils.putArgb(clearValues.get(i).color(), color);
304-
}
305-
}
306-
}
307-
if (hasDepth) {
308-
OptionalDouble clearDepth = depthAttachment.clearValue();
309-
if (clearDepth.isPresent()) {
310-
clearValues.get(colorTextures.length).depthStencil(VkClearDepthStencilValue.calloc(stack).depth((float)clearDepth.getAsDouble()));
311-
}
312-
}
280+
long framebuffer = this.device.framebufferCache().getOrCreateFramebuffer(renderPass, width, height, attachmentViews);
313281

314-
// Begin render pass
315-
org.lwjgl.vulkan.VkRenderPassBeginInfo renderPassBeginInfo = org.lwjgl.vulkan.VkRenderPassBeginInfo.calloc(stack).sType$Default();
316-
renderPassBeginInfo.renderPass(renderPass);
317-
renderPassBeginInfo.framebuffer(framebuffer);
318-
renderPassBeginInfo.renderArea().offset().set(descriptor.renderArea != null ? descriptor.renderArea.x() : 0, descriptor.renderArea != null ? descriptor.renderArea.y() : 0);
319-
renderPassBeginInfo.renderArea().extent().set(width, height);
320-
renderPassBeginInfo.pClearValues(clearValues);
321-
renderPassBeginInfo.clearValueCount(viewCount);
282+
// Begin render pass
283+
VkRenderPassBeginInfo renderPassBeginInfo = VkRenderPassBeginInfo.calloc(memoryStack).sType$Default();
284+
renderPassBeginInfo.renderPass(renderPass);
285+
renderPassBeginInfo.framebuffer(framebuffer);
286+
renderPassBeginInfo.renderArea().offset().set(descriptor.renderArea != null ? descriptor.renderArea.x() : 0, descriptor.renderArea != null ? descriptor.renderArea.y() : 0);
287+
renderPassBeginInfo.renderArea().extent().set(width, height);
288+
renderPassBeginInfo.pClearValues(clearValues);
289+
renderPassBeginInfo.clearValueCount(attachmentViews.length);
322290

323-
VK10.vkCmdBeginRenderPass(this.commandBuffer(), renderPassBeginInfo, VK10.VK_SUBPASS_CONTENTS_INLINE);
324-
}
291+
VK10.vkCmdBeginRenderPass(this.commandBuffer(), renderPassBeginInfo, VK10.VK_SUBPASS_CONTENTS_INLINE);
325292

326-
this.currentRenderPass = new Vk11RenderPass(
327-
this.device, this, this.commandBuffer(), descriptor.renderArea, width, height, depthAttachment != null, descriptor.label()
328-
);
329-
return this.currentRenderPass;
330-
}
293+
this.currentRenderPass = new Vk11RenderPass(
294+
this.device, this, this.commandBuffer(), descriptor.renderArea, width, height, hasDepth, descriptor.label()
295+
);
296+
return this.currentRenderPass;
297+
}
298+
}
331299

332300
private boolean poolCapacityLow() {
333301
for(Vk11DescriptorPool pool : descriptorPools) {

src/main/java/git/artdeell/artvk/Vk11Device.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import net.minecraft.resources.Identifier;
3939
import org.jetbrains.annotations.NotNull;
4040
import org.jspecify.annotations.Nullable;
41-
import org.lwjgl.util.vma.Vma;
4241
import org.lwjgl.vulkan.*;
4342

4443
@Environment(EnvType.CLIENT)
@@ -59,6 +58,7 @@ public class Vk11Device implements GpuDeviceBackend {
5958
public final boolean hasFillModeNonSolid, hasAnisotropy, hasAttributeDivisor;
6059
private final Vk11CommandEncoder commandEncoder;
6160
private final Vk11RenderPassCache renderPassCache;
61+
private final Vk11FramebufferCache framebufferCache;
6262

6363
public Vk11Device(
6464
final ShaderSource defaultShaderSource,
@@ -138,15 +138,17 @@ public Vk11Device(
138138
&& physicalDevice.vkPhysicalDeviceDriverProperties().driverID() == 14;
139139
physicalDevice.close();
140140
this.renderPassCache = new Vk11RenderPassCache(this);
141+
this.framebufferCache = new Vk11FramebufferCache(this);
141142
this.commandEncoder = new Vk11CommandEncoder(this);
142143
}
143144

144145
@Override
145146
public void close() {
146147
this.commandEncoder.destroy();
147148
this.clearPipelineCache();
149+
this.framebufferCache.destroy();
148150
this.renderPassCache.destroy();
149-
vmaObj.close();;
151+
vmaObj.close();
150152
VK10.vkDestroyDevice(this.vkDevice, null);
151153
this.instance.close();
152154
this.glslCompiler.close();
@@ -185,6 +187,10 @@ public Vk11RenderPassCache renderPassCache() {
185187
return this.renderPassCache;
186188
}
187189

190+
public Vk11FramebufferCache framebufferCache() {
191+
return this.framebufferCache;
192+
}
193+
188194
@Override
189195
public @NotNull GpuSurfaceBackend createSurface(final long windowHandle) {
190196
return new Vk11GpuSurface(this, windowHandle);
@@ -312,14 +318,15 @@ private Vk11RenderPipeline compilePipeline(final RenderPipeline pipeline, final
312318

313319
try {
314320
Vk11GlslCompiler.CompiledModules modules = this.glslCompiler.compile(this, pipeline, vertexShader, fragmentShader);
315-
List<Integer> colorFormats = new java.util.ArrayList<>();
316-
for (ColorTargetState cts : pipeline.getColorTargetStates()) {
317-
if (cts != null && cts.format() != null) {
318-
colorFormats.add(Vk11Const.toVk(cts.format()));
319-
} else {
320-
colorFormats.add(VK10.VK_FORMAT_R8G8B8A8_UNORM);
321-
}
322-
}
321+
ColorTargetState[] states = pipeline.getColorTargetStates();
322+
int[] colorFormats = new int[states.length];
323+
324+
for(int i = 0; i < states.length; i++) {
325+
ColorTargetState state = states[i];
326+
if(state != null) colorFormats[i] = Vk11Const.toVk(state.format());
327+
else colorFormats[i] = VK10.VK_FORMAT_UNDEFINED;
328+
}
329+
323330
int depthFormat = VK10.VK_FORMAT_D32_SFLOAT;
324331
long renderPassWithDepth = this.renderPassCache.getOrCreateRenderPass(colorFormats, true, depthFormat);
325332
long renderPassWithoutDepth = this.renderPassCache.getOrCreateRenderPass(colorFormats, false, VK10.VK_FORMAT_UNDEFINED);
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package git.artdeell.artvk;
2+
3+
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
4+
import org.lwjgl.system.MemoryStack;
5+
import org.lwjgl.vulkan.VK10;
6+
import org.lwjgl.vulkan.VkFramebufferCreateInfo;
7+
8+
import java.nio.LongBuffer;
9+
10+
public class Vk11FramebufferCache {
11+
private final Vk11Device device;
12+
private final Long2ObjectOpenHashMap<Framebuffer> framebufferCache = new Long2ObjectOpenHashMap<>();
13+
14+
public Vk11FramebufferCache(Vk11Device device) {
15+
this.device = device;
16+
}
17+
18+
private static long computeFramebufferKey(final long renderPass, final int width, final int height, final Vk11GpuTextureView[] views) {
19+
long key = renderPass;
20+
key = key * 31 + width;
21+
key = key * 31 + height;
22+
for (Vk11GpuTextureView view : views) {
23+
key = key * 31 + view.vkImageView();
24+
}
25+
return key;
26+
}
27+
28+
// In the future, when i maybe implement render pass cache cleanup, framebuffers would also be their dependents
29+
public long getOrCreateFramebuffer(
30+
final long renderPass,
31+
final int width,
32+
final int height,
33+
final Vk11GpuTextureView[] imageViews
34+
) {
35+
long key = computeFramebufferKey(renderPass, width, height, imageViews);
36+
Framebuffer cached = framebufferCache.get(key);
37+
if(cached != null) return cached.pointer;
38+
39+
Framebuffer framebuffer = new Framebuffer(key, renderPass, width, height, imageViews);
40+
for(Vk11GpuTextureView view : imageViews) {
41+
view.addDependent(framebuffer);
42+
}
43+
this.framebufferCache.put(key, framebuffer);
44+
return framebuffer.pointer;
45+
}
46+
47+
public void destroy() {
48+
for(Framebuffer framebuffer : framebufferCache.values()) framebuffer.destroy();
49+
}
50+
51+
private class Framebuffer implements Destroyable, Dependent {
52+
protected boolean isClosed = false;
53+
protected final long myKey;
54+
protected final long pointer;
55+
56+
public Framebuffer(long myKey, long renderPass, int width, int height, Vk11GpuTextureView[] views) {
57+
this.myKey = myKey;
58+
try (MemoryStack stack = MemoryStack.stackPush()) {
59+
VkFramebufferCreateInfo framebufferInfo = VkFramebufferCreateInfo.calloc(stack)
60+
.sType$Default()
61+
.renderPass(renderPass)
62+
.width(width)
63+
.height(height)
64+
.layers(1);
65+
66+
LongBuffer viewsBuffer = stack.callocLong(views.length);
67+
for(int i = 0; i < views.length; i++) {
68+
Vk11GpuTextureView view = views[i];
69+
viewsBuffer.put(i, view.vkImageView());
70+
view.addDependent(this);
71+
}
72+
73+
framebufferInfo.attachmentCount(views.length);
74+
framebufferInfo.pAttachments(viewsBuffer);
75+
76+
LongBuffer pointer = stack.callocLong(1);
77+
Vk11Utils.crashIfFailure(VK10.vkCreateFramebuffer(device.vkDevice(), framebufferInfo, null, pointer), "Failed to create VkFramebuffer");
78+
this.pointer = pointer.get(0);
79+
}
80+
}
81+
82+
@Override
83+
public void destroy() {
84+
VK10.vkDestroyFramebuffer(device.vkDevice(), pointer, null);
85+
}
86+
87+
@Override
88+
public void parentClosed() {
89+
if(isClosed) return;
90+
isClosed = true;
91+
framebufferCache.remove(myKey);
92+
device.createCommandEncoder().queueForDestroy(this);
93+
}
94+
}
95+
}

src/main/java/git/artdeell/artvk/Vk11GpuTextureView.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import com.mojang.blaze3d.textures.GpuTextureView;
44
import java.nio.LongBuffer;
5+
import java.util.HashSet;
6+
import java.util.Set;
7+
58
import net.fabricmc.api.EnvType;
69
import net.fabricmc.api.Environment;
710
import org.jetbrains.annotations.NotNull;
@@ -10,6 +13,7 @@
1013

1114
@Environment(EnvType.CLIENT)
1215
public class Vk11GpuTextureView extends GpuTextureView implements Destroyable {
16+
private final Set<Dependent> dependents = new HashSet<>();
1317
private final Vk11Device device;
1418
private final long vkImageView;
1519
private boolean closed;
@@ -39,8 +43,6 @@ protected Vk11GpuTextureView(final Vk11Device device, final Vk11GpuTexture textu
3943
texture.addViews();
4044
}
4145

42-
43-
4446
void enableTransferMode(MemoryStack memoryStack, VkCommandBuffer currentCommandBuffer) {
4547
texture().enableTransferMode(memoryStack, currentCommandBuffer, baseMipLevel());
4648
}
@@ -49,18 +51,22 @@ void disableTransferMode(MemoryStack memoryStack, VkCommandBuffer currentCommand
4951
texture().postTransferBarrier(memoryStack, currentCommandBuffer);
5052
}
5153

54+
public void addDependent(Dependent destroyable) {
55+
dependents.add(destroyable);
56+
}
57+
5258
@Override
5359
public void destroy() {
5460
VK10.vkDestroyImageView(this.device.vkDevice(), this.vkImageView, null);
5561
}
5662

5763
@Override
5864
public void close() {
59-
if (!this.closed) {
60-
this.closed = true;
61-
this.device.createCommandEncoder().queueForDestroy(this);
62-
this.texture().removeViews();
63-
}
65+
if(this.closed) return;
66+
this.closed = true;
67+
this.device.createCommandEncoder().queueForDestroy(this);
68+
this.texture().removeViews();
69+
Vk11Utils.parentClose(dependents);
6470
}
6571

6672
@Override

0 commit comments

Comments
 (0)