Framebuffer &FramebufferAllocator::request_framebuffer(const RenderPassInfo &info)
{
	auto &rp = device->request_render_pass(info);
	Hasher h;
	h.u64(rp.get_cookie());

	for (unsigned i = 0; i < info.num_color_attachments; i++)
		if (info.color_attachments[i])
			h.u64(info.color_attachments[i]->get_cookie());

	if (info.depth_stencil)
		h.u64(info.depth_stencil->get_cookie());

	auto hash = h.get();
	auto *node = framebuffers.request(hash);
	if (node)
		return *node;
	return *framebuffers.emplace(hash, device, rp, info);
}
void CommandBuffer::flush_graphics_pipeline()
{
	Hasher h;
	active_vbos = 0;
	auto &layout = current_layout->get_resource_layout();
	for_each_bit(layout.attribute_mask, [&](uint32_t bit) {
		h.u32(bit);
		active_vbos |= 1u << attribs[bit].binding;
		h.u32(attribs[bit].binding);
		h.u32(attribs[bit].format);
		h.u32(attribs[bit].offset);
	});

	for_each_bit(active_vbos, [&](uint32_t bit) {
		h.u32(vbo_input_rates[bit]);
		h.u32(vbo_strides[bit]);
	});

	h.u64(render_pass->get_cookie());
	h.u64(current_program->get_cookie());
	h.data(static_state.words, sizeof(static_state.words));

	if (static_state.state.blend_enable)
	{
		const auto needs_blend_constant = [](VkBlendFactor factor) {
			return factor == VK_BLEND_FACTOR_CONSTANT_COLOR || factor == VK_BLEND_FACTOR_CONSTANT_ALPHA;
		};
		bool b0 = needs_blend_constant(static_cast<VkBlendFactor>(static_state.state.src_color_blend));
		bool b1 = needs_blend_constant(static_cast<VkBlendFactor>(static_state.state.src_alpha_blend));
		bool b2 = needs_blend_constant(static_cast<VkBlendFactor>(static_state.state.dst_color_blend));
		bool b3 = needs_blend_constant(static_cast<VkBlendFactor>(static_state.state.dst_alpha_blend));
		if (b0 || b1 || b2 || b3)
			h.data(reinterpret_cast<uint32_t *>(potential_static_state.blend_constants),
			       sizeof(potential_static_state.blend_constants));
	}

	auto hash = h.get();
	current_pipeline = current_program->get_graphics_pipeline(hash);
	if (current_pipeline == VK_NULL_HANDLE)
		current_pipeline = build_graphics_pipeline(hash);
}
void CommandBuffer::flush_descriptor_set(uint32_t set)
{
	auto &layout = current_layout->get_resource_layout();
	auto &set_layout = layout.sets[set];
	uint32_t num_dynamic_offsets = 0;
	uint32_t dynamic_offsets[VULKAN_NUM_BINDINGS];
	Hasher h;

	// UBOs
	for_each_bit(set_layout.uniform_buffer_mask, [&](uint32_t binding) {
		h.u64(cookies[set][binding]);
		h.u32(bindings[set][binding].buffer.range);
		VK_ASSERT(bindings[set][binding].buffer.buffer != VK_NULL_HANDLE);

		dynamic_offsets[num_dynamic_offsets++] = bindings[set][binding].buffer.offset;
	});

	// SSBOs
	for_each_bit(set_layout.storage_buffer_mask, [&](uint32_t binding) {
		h.u64(cookies[set][binding]);
		h.u32(bindings[set][binding].buffer.offset);
		h.u32(bindings[set][binding].buffer.range);
		VK_ASSERT(bindings[set][binding].buffer.buffer != VK_NULL_HANDLE);
	});

	// Sampled buffers
	for_each_bit(set_layout.sampled_buffer_mask, [&](uint32_t binding) {
		h.u64(cookies[set][binding]);
		VK_ASSERT(bindings[set][binding].buffer_view != VK_NULL_HANDLE);
	});

	// Sampled images
	for_each_bit(set_layout.sampled_image_mask, [&](uint32_t binding) {
		h.u64(cookies[set][binding]);
		h.u64(secondary_cookies[set][binding]);
		h.u32(bindings[set][binding].image.imageLayout);
		VK_ASSERT(bindings[set][binding].image.imageView != VK_NULL_HANDLE);
		VK_ASSERT(bindings[set][binding].image.sampler != VK_NULL_HANDLE);
	});

	// Storage images
	for_each_bit(set_layout.storage_image_mask, [&](uint32_t binding) {
		h.u64(cookies[set][binding]);
		h.u32(bindings[set][binding].image.imageLayout);
		VK_ASSERT(bindings[set][binding].image.imageView != VK_NULL_HANDLE);
	});

	// Input attachments
	for_each_bit(set_layout.input_attachment_mask, [&](uint32_t binding) {
		h.u64(cookies[set][binding]);
		h.u32(bindings[set][binding].image.imageLayout);
		VK_ASSERT(bindings[set][binding].image.imageView != VK_NULL_HANDLE);
	});

	Hash hash = h.get();
	auto allocated = current_layout->get_allocator(set)->find(hash);

	// The descriptor set was not successfully cached, rebuild.
	if (!allocated.second)
	{
		uint32_t write_count = 0;
		uint32_t buffer_info_count = 0;
		VkWriteDescriptorSet writes[VULKAN_NUM_BINDINGS];
		VkDescriptorBufferInfo buffer_info[VULKAN_NUM_BINDINGS];

		for_each_bit(set_layout.uniform_buffer_mask, [&](uint32_t binding) {
			auto &write = writes[write_count++];
			write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
			write.pNext = nullptr;
			write.descriptorCount = 1;
			write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
			write.dstArrayElement = 0;
			write.dstBinding = binding;
			write.dstSet = allocated.first;

			// Offsets are applied dynamically.
			auto &buffer = buffer_info[buffer_info_count++];
			buffer = bindings[set][binding].buffer;
			buffer.offset = 0;
			write.pBufferInfo = &buffer;
		});

		for_each_bit(set_layout.storage_buffer_mask, [&](uint32_t binding) {
			auto &write = writes[write_count++];
			write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
			write.pNext = nullptr;
			write.descriptorCount = 1;
			write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
			write.dstArrayElement = 0;
			write.dstBinding = binding;
			write.dstSet = allocated.first;
			write.pBufferInfo = &bindings[set][binding].buffer;
		});

		for_each_bit(set_layout.sampled_buffer_mask, [&](uint32_t binding) {
			auto &write = writes[write_count++];
			write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
			write.pNext = nullptr;
			write.descriptorCount = 1;
			write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
			write.dstArrayElement = 0;
			write.dstBinding = binding;
			write.dstSet = allocated.first;
			write.pTexelBufferView = &bindings[set][binding].buffer_view;
		});

		for_each_bit(set_layout.sampled_image_mask, [&](uint32_t binding) {
			auto &write = writes[write_count++];
			write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
			write.pNext = nullptr;
			write.descriptorCount = 1;
			write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
			write.dstArrayElement = 0;
			write.dstBinding = binding;
			write.dstSet = allocated.first;
			write.pImageInfo = &bindings[set][binding].image;
		});

		for_each_bit(set_layout.storage_image_mask, [&](uint32_t binding) {
			auto &write = writes[write_count++];
			write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
			write.pNext = nullptr;
			write.descriptorCount = 1;
			write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
			write.dstArrayElement = 0;
			write.dstBinding = binding;
			write.dstSet = allocated.first;
			write.pImageInfo = &bindings[set][binding].image;
		});

		for_each_bit(set_layout.input_attachment_mask, [&](uint32_t binding) {
			auto &write = writes[write_count++];
			write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
			write.pNext = nullptr;
			write.descriptorCount = 1;
			write.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
			write.dstArrayElement = 0;
			write.dstBinding = binding;
			write.dstSet = allocated.first;
			write.pImageInfo = &bindings[set][binding].image;
		});

		vkUpdateDescriptorSets(device->get_device(), write_count, writes, 0, nullptr);
	}

	vkCmdBindDescriptorSets(cmd, render_pass ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
	                        current_pipeline_layout, set, 1, &allocated.first, num_dynamic_offsets, dynamic_offsets);
}