OverviewFilesCommitsIssuesDocsReleasesPeers

nvidia-rtx-godot / servers / rendering / renderer_rd / bindless_block.cpp

168 lines · cpp ref:
/**************************************************************************/
/*  bindless_block.cpp                                                    */
/**************************************************************************/
/*                         This file is part of:                          */
/*                             GODOT ENGINE                               */
/*                        https://godotengine.org                         */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
/*                                                                        */
/* Permission is hereby granted, free of charge, to any person obtaining  */
/* a copy of this software and associated documentation files (the        */
/* "Software"), to deal in the Software without restriction, including    */
/* without limitation the rights to use, copy, modify, merge, publish,    */
/* distribute, sublicense, and/or sell copies of the Software, and to     */
/* permit persons to whom the Software is furnished to do so, subject to  */
/* the following conditions:                                              */
/*                                                                        */
/* The above copyright notice and this permission notice shall be         */
/* included in all copies or substantial portions of the Software.        */
/*                                                                        */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
/**************************************************************************/

#include "bindless_block.h"

#include "servers/rendering/renderer_rd/storage_rd/texture_storage.h"

void BindlessBlock::initialize(RenderingDevice *p_rd, TextureKind p_kind) {
	ERR_FAIL_NULL(p_rd);

	rd = p_rd;
	kind = p_kind;

	RendererRD::TextureStorage::DefaultRDTexture default_id = RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_WHITE;
	switch (kind) {
		case TEXTURE_2D_ARRAY:
			default_id = RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE;
			break;
		case TEXTURE_3D:
			default_id = RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE;
			break;
		case TEXTURE_CUBE:
			default_id = RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_WHITE;
			break;
		case TEXTURE_CUBE_ARRAY:
			default_id = RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_WHITE;
			break;
		default:
			break;
	}

	default_texture = RendererRD::TextureStorage::get_singleton()->texture_rd_get_default(default_id);
	textures.push_back(default_texture);
	texture_to_index[default_texture] = 0;
	needs_refinalize = true;
}

void BindlessBlock::begin_frame(bool p_set_valid) {
	if (!is_initialized()) {
		return;
	}

	if (p_set_valid) {
		return;
	}

	// Uniform set was externally invalidated (a texture was destroyed/recreated).
	// Scrub dead RIDs: replace with default and add slots to the freelist.
	texture_to_index.clear();
	texture_to_index[default_texture] = 0;

	for (uint32_t i = 1; i < textures.size(); i++) {
		if (!rd->texture_is_valid(textures[i])) {
			textures[i] = default_texture;
			if (free_indices_count < free_indices.size()) {
				free_indices[free_indices_count] = i;
			} else {
				free_indices.push_back(i);
			}
			++free_indices_count;
		} else {
			texture_to_index[textures[i]] = i;
		}
	}

	needs_refinalize = true;
}

uint32_t BindlessBlock::add_texture(RID p_texture) {
	ERR_FAIL_COND_V_MSG(!is_initialized(), 0, "BindlessBlock not initialized. Call initialize() first.");

	// Substitute null/stale RIDs with the default white texture.
	if (!p_texture.is_valid() || !rd->texture_is_valid(p_texture)) {
		p_texture = default_texture;
	}

	HashMap<RID, uint32_t>::Iterator it = texture_to_index.find(p_texture);
	if (it != texture_to_index.end()) {
		return it->value;
	}

	const uint32_t capacity = is_unbounded(kind) ? MAX_BINDLESS_TEXTURES : TYPED_TABLE_CAPACITY;
	if (textures.size() >= capacity && free_indices_count == 0) {
		ERR_PRINT_ONCE("BindlessBlock: Maximum texture count exceeded. Using default texture.");
		return 0;
	}

	needs_refinalize = true;

	uint32_t index;
	if (free_indices_count > 0) {
		--free_indices_count;
		index = free_indices[free_indices_count];
		textures[index] = p_texture;
	} else {
		index = textures.size();
		textures.push_back(p_texture);
	}

	texture_to_index[p_texture] = index;
	return index;
}

void BindlessBlock::collect_uniform(Vector<RD::Uniform> &r_uniforms) const {
	ERR_FAIL_COND_MSG(!is_initialized(), "BindlessBlock not initialized.");
	ERR_FAIL_COND_MSG(textures.is_empty(), "BindlessBlock has no textures.");

	RD::Uniform u;
	u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
	u.binding = get_binding(kind);
	u.variable_count = is_unbounded(kind);

	for (uint32_t i = 0; i < textures.size(); i++) {
		u.append_id(textures[i]);
	}

	// A bounded binding gets no PARTIALLY_BOUND flag, so every descriptor in the
	// declared array must be written; pad the tail with this kind's default.
	if (!is_unbounded(kind)) {
		for (uint32_t i = textures.size(); i < TYPED_TABLE_CAPACITY; i++) {
			u.append_id(default_texture);
		}
	}

	r_uniforms.push_back(u);
}

void BindlessBlock::clear() {
	// The uniform set is shared across kinds and owned by the caller.
	textures.clear();
	texture_to_index.clear();
	free_indices.clear();
	free_indices_count = 0;
	needs_refinalize = true;
	rd = nullptr;
}

BindlessBlock::~BindlessBlock() {
	clear();
}