WIP adding directory structure

This commit is contained in:
2024-08-17 23:41:58 -07:00
parent 7e4fc361d4
commit 77b75311e6
39 changed files with 23 additions and 29 deletions

View File

@@ -32,7 +32,7 @@
#include "KREngine-common.h"
#include "KRResource.h"
#include "KRScene.h"
#include "scene/KRScene.h"
#include "KRResource+blend.h"
#include "mimir.h"

View File

@@ -32,7 +32,7 @@
#include "KREngine-common.h"
#include "KRMaterial.h"
#include "KRTextureManager.h"
#include "resources/texture/KRTextureManager.h"
#include "KRRenderPass.h"
#include "KRContext.h"

View File

@@ -35,12 +35,12 @@
#include "KREngine-common.h"
#include "KRTexture.h"
#include "resources/texture/KRTexture.h"
#include "KRPipelineManager.h"
#include "KRPipeline.h"
#include "KRCamera.h"
#include "resources/KRResource.h"
#include "KRScene.h"
#include "resources/scene/KRScene.h"
#include "KRBone.h"
enum class CullMode : __uint32_t;

View File

@@ -36,7 +36,7 @@
#include "resources/KRResourceManager.h"
#include "KRMaterial.h"
#include "KRTextureManager.h"
#include "resources/texture/KRTextureManager.h"
#include "KRMaterialManager.h"
class KRMaterial;

View File

@@ -0,0 +1,600 @@
//
// KRScene.cpp
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#include "KREngine-common.h"
#include "KRLight.h"
#include "KRScene.h"
#include "KRNode.h"
#include "KRDirectionalLight.h"
#include "KRSpotLight.h"
#include "KRPointLight.h"
#include "resources/audio/KRAudioManager.h"
#include "KRRenderPass.h"
using namespace mimir;
using namespace hydra;
const long KRENGINE_OCCLUSION_TEST_EXPIRY = 10;
KRScene::KRScene(KRContext& context, std::string name) : KRResource(context, name)
{
m_pFirstLight = NULL;
m_pRootNode = new KRNode(*this, "scene_root");
notify_sceneGraphCreate(m_pRootNode);
}
KRScene::~KRScene()
{
delete m_pRootNode;
m_pRootNode = NULL;
}
void KRScene::renderFrame(VkCommandBuffer& commandBuffer, KRSurface& surface, float deltaTime)
{
getContext().startFrame(deltaTime);
KRCamera* camera = find<KRCamera>("default_camera");
if (camera == NULL) {
// Add a default camera if none are present
camera = new KRCamera(*this, "default_camera");
m_pRootNode->appendChild(camera);
}
// FINDME - This should be moved to de-couple Siren from the Rendering pipeline
getContext().getAudioManager()->setEnableAudio(camera->settings.siren_enable);
getContext().getAudioManager()->setEnableHRTF(camera->settings.siren_enable_hrtf);
getContext().getAudioManager()->setEnableReverb(camera->settings.siren_enable_reverb);
getContext().getAudioManager()->setReverbMaxLength(camera->settings.siren_reverb_max_length);
getContext().getTextureManager()->setMaxAnisotropy(camera->settings.max_anisotropy);
camera->renderFrame(commandBuffer, surface);
getContext().endFrame(deltaTime);
physicsUpdate(deltaTime);
}
std::set<KRAmbientZone*>& KRScene::getAmbientZones()
{
// FINDME, TODO - To support large scenes with many reverb / ambient zones, this function should take a KRAABB and cull out any far away zones
return m_ambientZoneNodes;
}
std::set<KRReverbZone*>& KRScene::getReverbZones()
{
// FINDME, TODO - To support large scenes with many reverb / ambient zones, this function should take a KRAABB and cull out any far away zones
return m_reverbZoneNodes;
}
std::set<KRLocator*>& KRScene::getLocators()
{
return m_locatorNodes;
}
std::set<KRLight*>& KRScene::getLights()
{
return m_lights;
}
void KRScene::render(VkCommandBuffer& commandBuffer, KRSurface& surface, KRCamera* pCamera, unordered_map<AABB, int>& visibleBounds, const KRViewport& viewport, const KRRenderPass* renderPass, bool new_frame)
{
if (new_frame) {
// Expire cached occlusion test results.
// Cached "failed" results are expired on the next frame (marked with .second of -1)
// Cached "success" results are expired after KRENGINE_OCCLUSION_TEST_EXPIRY frames (marked with .second of the last frame
std::set<AABB> expired_visible_bounds;
for (unordered_map<AABB, int>::iterator visible_bounds_itr = visibleBounds.begin(); visible_bounds_itr != visibleBounds.end(); visible_bounds_itr++) {
if ((*visible_bounds_itr).second == -1 || (*visible_bounds_itr).second + KRENGINE_OCCLUSION_TEST_EXPIRY < getContext().getCurrentFrame()) {
expired_visible_bounds.insert((*visible_bounds_itr).first);
}
}
for (std::set<AABB>::iterator expired_visible_bounds_itr = expired_visible_bounds.begin(); expired_visible_bounds_itr != expired_visible_bounds.end(); expired_visible_bounds_itr++) {
visibleBounds.erase(*expired_visible_bounds_itr);
}
}
if (getFirstLight() == NULL) {
addDefaultLights();
}
KRNode::RenderInfo ri(commandBuffer);
ri.camera = pCamera;
ri.viewport = viewport;
ri.renderPass = renderPass;
std::vector<KRPointLight*> point_lights;
std::vector<KRDirectionalLight*>directional_lights;
std::vector<KRSpotLight*>spot_lights;
std::set<KRNode*> outerNodes = std::set<KRNode*>(m_nodeTree.getOuterSceneNodes()); // HACK - Copying the std::set as it is potentially modified as KRNode's update their bounds during the iteration. This is very expensive and will be eliminated in the future.
// Get lights from outer nodes (directional lights, which have no bounds)
for (std::set<KRNode*>::iterator itr = outerNodes.begin(); itr != outerNodes.end(); itr++) {
KRNode* node = (*itr);
KRPointLight* point_light = dynamic_cast<KRPointLight*>(node);
if (point_light) {
point_lights.push_back(point_light);
}
KRDirectionalLight* directional_light = dynamic_cast<KRDirectionalLight*>(node);
if (directional_light) {
directional_lights.push_back(directional_light);
}
KRSpotLight* spot_light = dynamic_cast<KRSpotLight*>(node);
if (spot_light) {
spot_lights.push_back(spot_light);
}
}
// Render outer nodes
for (std::set<KRNode*>::iterator itr = outerNodes.begin(); itr != outerNodes.end(); itr++) {
KRNode* node = (*itr);
node->render(ri);
}
std::vector<KROctreeNode*> remainingOctrees;
std::vector<KROctreeNode*> remainingOctreesTestResults;
std::vector<KROctreeNode*> remainingOctreesTestResultsOnly;
if (m_nodeTree.getRootNode() != NULL) {
remainingOctrees.push_back(m_nodeTree.getRootNode());
}
std::vector<KROctreeNode*> newRemainingOctrees;
std::vector<KROctreeNode*> newRemainingOctreesTestResults;
while ((!remainingOctrees.empty() || !remainingOctreesTestResults.empty())) {
newRemainingOctrees.clear();
newRemainingOctreesTestResults.clear();
for (std::vector<KROctreeNode*>::iterator octree_itr = remainingOctrees.begin(); octree_itr != remainingOctrees.end(); octree_itr++) {
render(ri, *octree_itr, visibleBounds, newRemainingOctrees, newRemainingOctreesTestResults, remainingOctreesTestResultsOnly, false, false);
}
for (std::vector<KROctreeNode*>::iterator octree_itr = remainingOctreesTestResults.begin(); octree_itr != remainingOctreesTestResults.end(); octree_itr++) {
render(ri, *octree_itr, visibleBounds, newRemainingOctrees, newRemainingOctreesTestResults, remainingOctreesTestResultsOnly, true, false);
}
remainingOctrees = newRemainingOctrees;
remainingOctreesTestResults = newRemainingOctreesTestResults;
}
newRemainingOctrees.clear();
newRemainingOctreesTestResults.clear();
for (std::vector<KROctreeNode*>::iterator octree_itr = remainingOctreesTestResultsOnly.begin(); octree_itr != remainingOctreesTestResultsOnly.end(); octree_itr++) {
render(ri, *octree_itr, visibleBounds, newRemainingOctrees, newRemainingOctreesTestResults, remainingOctreesTestResultsOnly, true, true);
}
}
void KRScene::render(KRNode::RenderInfo& ri, KROctreeNode* pOctreeNode, unordered_map<AABB, int>& visibleBounds, std::vector<KROctreeNode*>& remainingOctrees, std::vector<KROctreeNode*>& remainingOctreesTestResults, std::vector<KROctreeNode*>& remainingOctreesTestResultsOnly, bool bOcclusionResultsPass, bool bOcclusionTestResultsOnly)
{
if (pOctreeNode) {
AABB octreeBounds = pOctreeNode->getBounds();
if (bOcclusionResultsPass) {
// ----====---- Occlusion results pass ----====----
if (pOctreeNode->m_occlusionTested) {
int params = 0;
GLDEBUG(glGetQueryObjectuivEXT(pOctreeNode->m_occlusionQuery, GL_QUERY_RESULT_EXT, &params));
if (params) {
// Record the frame number that the test has passed on
visibleBounds[octreeBounds] = getContext().getCurrentFrame();
if (!bOcclusionTestResultsOnly) {
// Schedule a pass to perform the rendering
remainingOctrees.push_back(pOctreeNode);
}
} else {
// Record -1 to indicate that the visibility test had failed
visibleBounds[octreeBounds] = -1;
}
GLDEBUG(glDeleteQueriesEXT(1, &pOctreeNode->m_occlusionQuery));
pOctreeNode->m_occlusionTested = false;
pOctreeNode->m_occlusionQuery = 0;
}
} else {
bool in_viewport = false;
if (ri.renderPass->getType() == RenderPassType::RENDER_PASS_PRESTREAM) {
// When pre-streaming, objects are streamed in behind and in-front of the camera
AABB viewportExtents = AABB::Create(ri.viewport.getCameraPosition() - Vector3::Create(ri.camera->settings.getPerspectiveFarZ()), ri.viewport.getCameraPosition() + Vector3::Create(ri.camera->settings.getPerspectiveFarZ()));
in_viewport = octreeBounds.intersects(viewportExtents);
} else {
in_viewport = ri.viewport.visible(pOctreeNode->getBounds());
}
if (in_viewport) {
// ----====---- Rendering and occlusion test pass ----====----
bool bVisible = false;
bool bNeedOcclusionTest = true;
if (!ri.camera->settings.getEnableRealtimeOcclusion()) {
bVisible = true;
bNeedOcclusionTest = false;
}
if (!bVisible) {
// Assume bounding boxes are visible without occlusion test queries if the camera is inside the box.
// The near clipping plane of the camera is taken into consideration by expanding the match area
AABB cameraExtents = AABB::Create(ri.viewport.getCameraPosition() - Vector3::Create(ri.camera->settings.getPerspectiveNearZ()), ri.viewport.getCameraPosition() + Vector3::Create(ri.camera->settings.getPerspectiveNearZ()));
bVisible = octreeBounds.intersects(cameraExtents);
if (bVisible) {
// Record the frame number in which the camera was within the bounds
visibleBounds[octreeBounds] = getContext().getCurrentFrame();
bNeedOcclusionTest = false;
}
}
if (!bVisible) {
// Check if a previous occlusion query has returned true, taking advantage of temporal consistency of visible elements from frame to frame
// If the previous frame rendered this octree, then attempt to render it in this frame without performing a pre-occlusion test
unordered_map<AABB, int>::iterator match_itr = visibleBounds.find(octreeBounds);
if (match_itr != visibleBounds.end()) {
if ((*match_itr).second == -1) {
// We have already tested these bounds with a negative result
bNeedOcclusionTest = false;
} else {
bVisible = true;
// We set bNeedOcclusionTest to false only when the previous occlusion test is old and we need to perform an occlusion test to record if this octree node was visible for the next frame
bNeedOcclusionTest = false;
}
}
}
if (!bVisible && bNeedOcclusionTest) {
// Optimization: If this is an empty octree node with only a single child node, then immediately try to render the child node without an occlusion test for this higher level, as it would be more expensive than the occlusion test for the child
if (pOctreeNode->getSceneNodes().empty()) {
int child_count = 0;
for (int i = 0; i < 8; i++) {
if (pOctreeNode->getChildren()[i] != NULL) child_count++;
}
if (child_count == 1) {
bVisible = true;
bNeedOcclusionTest = false;
}
}
}
if (bNeedOcclusionTest) {
pOctreeNode->beginOcclusionQuery();
Matrix4 matModel = Matrix4();
matModel.scale(octreeBounds.size() * 0.5f);
matModel.translate(octreeBounds.center());
Matrix4 mvpmatrix = matModel * ri.viewport.getViewProjectionMatrix();
KRMeshManager::KRVBOData& vertices = getContext().getMeshManager()->KRENGINE_VBO_DATA_3D_CUBE_VERTICES;
getContext().getMeshManager()->bindVBO(ri.commandBuffer, &vertices, 1.0f);
PipelineInfo info{};
std::string shader_name("occlusion_test");
info.shader_name = &shader_name;
info.pCamera = ri.camera;
info.point_lights = &ri.point_lights;
info.directional_lights = &ri.directional_lights;
info.spot_lights = &ri.spot_lights;
info.renderPass = ri.renderPass;
info.rasterMode = RasterMode::kAdditive;
info.vertexAttributes = vertices.getVertexAttributes();
info.modelFormat = ModelFormat::KRENGINE_MODEL_FORMAT_STRIP;
KRPipeline* pPipeline = getContext().getPipelineManager()->getPipeline(*ri.surface, info);
pPipeline->bind(ri.commandBuffer, *info.pCamera, ri.viewport, matModel, info.point_lights, info.directional_lights, info.spot_lights, info.renderPass);
vkCmdDraw(ri.commandBuffer, 14, 1, 0, 0);
m_pContext->getMeshManager()->log_draw_call(ri.renderPass->getType(), "octree", "occlusion_test", 14);
pOctreeNode->endOcclusionQuery();
if (bVisible) {
// Schedule a pass to get the result of the occlusion test only for future frames and passes, without rendering the model or recurring further
remainingOctreesTestResultsOnly.push_back(pOctreeNode);
} else {
// Schedule a pass to get the result of the occlusion test and continue recursion and rendering if test is true
remainingOctreesTestResults.push_back(pOctreeNode);
}
}
if (bVisible) {
// Add lights that influence this octree level and its children to the stack
int directional_light_count = 0;
int spot_light_count = 0;
int point_light_count = 0;
for (std::set<KRNode*>::iterator itr = pOctreeNode->getSceneNodes().begin(); itr != pOctreeNode->getSceneNodes().end(); itr++) {
KRNode* node = (*itr);
KRDirectionalLight* directional_light = dynamic_cast<KRDirectionalLight*>(node);
if (directional_light) {
ri.directional_lights.push_back(directional_light);
directional_light_count++;
}
KRSpotLight* spot_light = dynamic_cast<KRSpotLight*>(node);
if (spot_light) {
ri.spot_lights.push_back(spot_light);
spot_light_count++;
}
KRPointLight* point_light = dynamic_cast<KRPointLight*>(node);
if (point_light) {
ri.point_lights.push_back(point_light);
point_light_count++;
}
}
// Render objects that are at this octree level
for (std::set<KRNode*>::iterator itr = pOctreeNode->getSceneNodes().begin(); itr != pOctreeNode->getSceneNodes().end(); itr++) {
//assert(pOctreeNode->getBounds().contains((*itr)->getBounds())); // Sanity check
(*itr)->render(ri);
}
// Render child octrees
const int* childOctreeOrder = ri.renderPass->getType() == RenderPassType::RENDER_PASS_FORWARD_TRANSPARENT || ri.renderPass->getType() == RenderPassType::RENDER_PASS_ADDITIVE_PARTICLES || ri.renderPass->getType() == RenderPassType::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE ? ri.viewport.getBackToFrontOrder() : ri.viewport.getFrontToBackOrder();
for (int i = 0; i < 8; i++) {
render(ri, pOctreeNode->getChildren()[childOctreeOrder[i]], visibleBounds, remainingOctrees, remainingOctreesTestResults, remainingOctreesTestResultsOnly, false, false);
}
// Remove lights added at this octree level from the stack
while (directional_light_count--) {
ri.directional_lights.pop_back();
}
while (spot_light_count--) {
ri.spot_lights.pop_back();
}
while (point_light_count--) {
ri.point_lights.pop_back();
}
}
}
}
}
// fprintf(stderr, "Octree culled: (%f, %f, %f) - (%f, %f, %f)\n", pOctreeNode->getBounds().min.x, pOctreeNode->getBounds().min.y, pOctreeNode->getBounds().min.z, pOctreeNode->getBounds().max.x, pOctreeNode->getBounds().max.y, pOctreeNode->getBounds().max.z);
}
std::string KRScene::getExtension()
{
return "krscene";
}
KRNode* KRScene::getRootNode()
{
return m_pRootNode;
}
bool KRScene::save(Block& data)
{
tinyxml2::XMLDocument doc;
tinyxml2::XMLElement* scene_node = doc.NewElement("scene");
doc.InsertEndChild(scene_node);
m_pRootNode->saveXML(scene_node);
tinyxml2::XMLPrinter p;
doc.Print(&p);
data.append((void*)p.CStr(), strlen(p.CStr()) + 1);
return true;
}
KRScene* KRScene::Load(KRContext& context, const std::string& name, Block* data)
{
std::string xml_string = data->getString();
delete data;
tinyxml2::XMLDocument doc;
doc.Parse(xml_string.c_str());
KRScene* new_scene = new KRScene(context, name);
tinyxml2::XMLElement* scene_element = doc.RootElement();
KRNode* n = KRNode::LoadXML(*new_scene, scene_element->FirstChildElement());
if (n) {
new_scene->getRootNode()->appendChild(n);
}
return new_scene;
}
KRLight* KRScene::getFirstLight()
{
if (m_pFirstLight == NULL) {
m_pFirstLight = find<KRLight>();
}
return m_pFirstLight;
}
void KRScene::notify_sceneGraphCreate(KRNode* pNode)
{
// m_nodeTree.add(pNode);
// if(pNode->hasPhysics()) {
// m_physicsNodes.insert(pNode);
// }
m_newNodes.insert(pNode);
}
void KRScene::notify_sceneGraphModify(KRNode* pNode)
{
// m_nodeTree.update(pNode);
m_modifiedNodes.insert(pNode);
}
void KRScene::notify_sceneGraphDelete(KRNode* pNode)
{
m_nodeTree.remove(pNode);
m_physicsNodes.erase(pNode);
KRAmbientZone* AmbientZoneNode = dynamic_cast<KRAmbientZone*>(pNode);
if (AmbientZoneNode) {
m_ambientZoneNodes.erase(AmbientZoneNode);
}
KRReverbZone* ReverbZoneNode = dynamic_cast<KRReverbZone*>(pNode);
if (ReverbZoneNode) {
m_reverbZoneNodes.erase(ReverbZoneNode);
}
KRLocator* locator = dynamic_cast<KRLocator*>(pNode);
if (locator) {
m_locatorNodes.erase(locator);
}
KRLight* light = dynamic_cast<KRLight*>(pNode);
if (light) {
m_lights.erase(light);
}
m_modifiedNodes.erase(pNode);
if (!m_newNodes.erase(pNode)) {
m_nodeTree.remove(pNode);
}
}
void KRScene::updateOctree(const KRViewport& viewport)
{
m_pRootNode->setLODVisibility(KRNode::LOD_VISIBILITY_VISIBLE);
m_pRootNode->updateLODVisibility(viewport);
std::set<KRNode*> newNodes = std::move(m_newNodes);
std::set<KRNode*> modifiedNodes = std::move(m_modifiedNodes);
m_newNodes.clear();
m_modifiedNodes.clear();
for (std::set<KRNode*>::iterator itr = newNodes.begin(); itr != newNodes.end(); itr++) {
KRNode* node = *itr;
m_nodeTree.add(node);
if (node->hasPhysics()) {
m_physicsNodes.insert(node);
}
KRAmbientZone* ambientZoneNode = dynamic_cast<KRAmbientZone*>(node);
if (ambientZoneNode) {
m_ambientZoneNodes.insert(ambientZoneNode);
}
KRReverbZone* reverbZoneNode = dynamic_cast<KRReverbZone*>(node);
if (reverbZoneNode) {
m_reverbZoneNodes.insert(reverbZoneNode);
}
KRLocator* locatorNode = dynamic_cast<KRLocator*>(node);
if (locatorNode) {
m_locatorNodes.insert(locatorNode);
}
KRLight* light = dynamic_cast<KRLight*>(node);
if (light) {
m_lights.insert(light);
}
}
for (std::set<KRNode*>::iterator itr = modifiedNodes.begin(); itr != modifiedNodes.end(); itr++) {
KRNode* node = *itr;
if (node->getLODVisibility() >= KRNode::LOD_VISIBILITY_PRESTREAM) {
m_nodeTree.update(node);
}
if (node->hasPhysics()) {
m_physicsNodes.insert(node);
} else if (!node->hasPhysics()) {
m_physicsNodes.erase(node);
}
}
}
void KRScene::buildOctreeForTheFirstTime()
{
std::set<KRNode*> newNodes = std::move(m_newNodes);
m_newNodes.clear();
for (std::set<KRNode*>::iterator itr = newNodes.begin(); itr != newNodes.end(); itr++) {
KRNode* node = *itr;
m_nodeTree.add(node);
if (node->hasPhysics()) {
m_physicsNodes.insert(node);
}
KRAmbientZone* ambientZoneNode = dynamic_cast<KRAmbientZone*>(node);
if (ambientZoneNode) {
m_ambientZoneNodes.insert(ambientZoneNode);
}
KRReverbZone* reverbZoneNode = dynamic_cast<KRReverbZone*>(node);
if (reverbZoneNode) {
m_reverbZoneNodes.insert(reverbZoneNode);
}
KRLocator* locatorNode = dynamic_cast<KRLocator*>(node);
if (locatorNode) {
m_locatorNodes.insert(locatorNode);
}
KRLight* light = dynamic_cast<KRLight*>(node);
if (light) {
m_lights.insert(light);
}
}
}
void KRScene::physicsUpdate(float deltaTime)
{
for (std::set<KRNode*>::iterator itr = m_physicsNodes.begin(); itr != m_physicsNodes.end(); itr++) {
(*itr)->physicsUpdate(deltaTime);
}
}
void KRScene::addDefaultLights()
{
KRDirectionalLight* light1 = new KRDirectionalLight(*this, "default_light1");
light1->setLocalRotation((Quaternion::Create(Vector3::Create(0.0f, (float)M_PI * 0.10f, 0.0f)) * Quaternion::Create(Vector3::Create(0.0f, 0.0f, (float)-M_PI * 0.15f))).eulerXYZ());
m_pRootNode->appendChild(light1);
}
AABB KRScene::getRootOctreeBounds()
{
if (m_nodeTree.getRootNode()) {
return m_nodeTree.getRootNode()->getBounds();
} else {
return AABB::Create(-Vector3::One(), Vector3::One());
}
}
bool KRScene::lineCast(const Vector3& v0, const Vector3& v1, HitInfo& hitinfo, unsigned int layer_mask)
{
return m_nodeTree.lineCast(v0, v1, hitinfo, layer_mask);
}
bool KRScene::rayCast(const Vector3& v0, const Vector3& dir, HitInfo& hitinfo, unsigned int layer_mask)
{
return m_nodeTree.rayCast(v0, dir, hitinfo, layer_mask);
}
bool KRScene::sphereCast(const Vector3& v0, const Vector3& v1, float radius, HitInfo& hitinfo, unsigned int layer_mask)
{
return m_nodeTree.sphereCast(v0, v1, radius, hitinfo, layer_mask);
}
kraken_stream_level KRScene::getStreamLevel()
{
kraken_stream_level stream_level = kraken_stream_level::STREAM_LEVEL_IN_HQ;
if (m_pRootNode) {
KRViewport viewport; // This isn't used when prime is false
stream_level = KRMIN(stream_level, m_pRootNode->getStreamLevel(viewport));
}
return stream_level;
}

125
kraken/resources/scene/KRScene.h Executable file
View File

@@ -0,0 +1,125 @@
//
// KRScene.h
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#pragma once
#include "KREngine-common.h"
#include "KRModel.h"
#include "resources/mesh/KRMesh.h"
#include "KRCamera.h"
#include "resources/mesh/KRMeshManager.h"
#include "KRNode.h"
#include "KRLocator.h"
#include "KRAmbientZone.h"
#include "KRReverbZone.h"
#include "KROctree.h"
class KRModel;
class KRLight;
class KRSurface;
using std::vector;
class KRScene : public KRResource
{
public:
KRScene(KRContext& context, std::string name);
virtual ~KRScene();
virtual std::string getExtension();
virtual bool save(mimir::Block& data);
static KRScene* Load(KRContext& context, const std::string& name, mimir::Block* data);
KRNode* getRootNode();
KRLight* getFirstLight();
kraken_stream_level getStreamLevel();
bool lineCast(const hydra::Vector3& v0, const hydra::Vector3& v1, hydra::HitInfo& hitinfo, unsigned int layer_mask);
bool rayCast(const hydra::Vector3& v0, const hydra::Vector3& dir, hydra::HitInfo& hitinfo, unsigned int layer_mask);
bool sphereCast(const hydra::Vector3& v0, const hydra::Vector3& v1, float radius, hydra::HitInfo& hitinfo, unsigned int layer_mask);
void renderFrame(VkCommandBuffer& commandBuffer, KRSurface& surface, float deltaTime);
void render(VkCommandBuffer& commandBuffer, KRSurface& surface, KRCamera* pCamera, unordered_map<hydra::AABB, int>& visibleBounds, const KRViewport& viewport, const KRRenderPass* renderPass, bool new_frame);
void render(KRNode::RenderInfo& ri, KROctreeNode* pOctreeNode, unordered_map<hydra::AABB, int>& visibleBounds, std::vector<KROctreeNode*>& remainingOctrees, std::vector<KROctreeNode*>& remainingOctreesTestResults, std::vector<KROctreeNode*>& remainingOctreesTestResultsOnly, bool bOcclusionResultsPass, bool bOcclusionTestResultsOnly);
void updateOctree(const KRViewport& viewport);
void buildOctreeForTheFirstTime();
void notify_sceneGraphCreate(KRNode* pNode);
void notify_sceneGraphDelete(KRNode* pNode);
void notify_sceneGraphModify(KRNode* pNode);
void physicsUpdate(float deltaTime);
void addDefaultLights();
hydra::AABB getRootOctreeBounds();
std::set<KRAmbientZone*>& getAmbientZones();
std::set<KRReverbZone*>& getReverbZones();
std::set<KRLocator*>& getLocators();
std::set<KRLight*>& getLights();
private:
KRNode* m_pRootNode;
KRLight* m_pFirstLight;
std::set<KRNode*> m_newNodes;
std::set<KRNode*> m_modifiedNodes;
std::set<KRNode*> m_physicsNodes;
std::set<KRAmbientZone*> m_ambientZoneNodes;
std::set<KRReverbZone*> m_reverbZoneNodes;
std::set<KRLocator*> m_locatorNodes;
std::set<KRLight*> m_lights;
KROctree m_nodeTree;
public:
template <class T> T* find()
{
if (m_pRootNode) return m_pRootNode->find<T>();
return NULL;
}
template <class T> T* find(const std::string& name)
{
if (m_pRootNode) return m_pRootNode->find<T>(name);
return NULL;
}
};

View File

@@ -0,0 +1,124 @@
//
// KRSceneManager.cpp
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#include "KRSceneManager.h"
#include "KRScene.h"
using namespace mimir;
KRSceneManager::KRSceneManager(KRContext& context) : KRResourceManager(context)
{}
KRSceneManager::~KRSceneManager()
{
for (unordered_map<std::string, KRScene*>::iterator itr = m_scenes.begin(); itr != m_scenes.end(); ++itr) {
delete (*itr).second;
}
m_scenes.clear();
}
KRResource* KRSceneManager::loadResource(const std::string& name, const std::string& extension, Block* data)
{
if (extension.compare("krscene") == 0) {
return loadScene(name, data);
}
return nullptr;
}
KRResource* KRSceneManager::getResource(const std::string& name, const std::string& extension)
{
if (extension.compare("krscene") == 0) {
return getScene(name);
}
return nullptr;
}
KRScene* KRSceneManager::loadScene(const std::string& name, Block* data)
{
std::lock_guard<std::mutex> lock(m_mutex);
std::string lowerName = name;
std::transform(lowerName.begin(), lowerName.end(),
lowerName.begin(), ::tolower);
KRScene* pScene = KRScene::Load(*m_pContext, name, data);
m_scenes[lowerName] = pScene;
return pScene;
}
KRScene* KRSceneManager::createScene(const std::string& name)
{
// TODO: Check for name conflicts
KRScene* pScene = new KRScene(*m_pContext, name);
add(pScene);
return pScene;
}
void KRSceneManager::add(KRScene* scene)
{
std::lock_guard<std::mutex> lock(m_mutex);
std::string lowerName = scene->getName();
std::transform(lowerName.begin(), lowerName.end(),
lowerName.begin(), ::tolower);
m_scenes[lowerName] = scene;
}
KRScene* KRSceneManager::getScene(const std::string& name)
{
std::lock_guard<std::mutex> lock(m_mutex);
std::string lowerName = name;
std::transform(lowerName.begin(), lowerName.end(),
lowerName.begin(), ::tolower);
static unordered_map<std::string, KRScene*>::iterator scene_itr = m_scenes.find(lowerName);
if (scene_itr != m_scenes.end()) {
return (*scene_itr).second;
} else {
return NULL;
}
}
KRScene* KRSceneManager::getFirstScene()
{
std::lock_guard<std::mutex> lock(m_mutex);
unordered_map<std::string, KRScene*>::iterator scene_itr = m_scenes.begin();
if (scene_itr != m_scenes.end()) {
return (*scene_itr).second;
} else {
return NULL;
}
}
unordered_map<std::string, KRScene*>& KRSceneManager::getScenes()
{
return m_scenes;
}

View File

@@ -0,0 +1,67 @@
//
// KRSceneManager.h
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#pragma once
#include "KREngine-common.h"
#include "resources/KRResourceManager.h"
#include "KRContextObject.h"
#include "block.h"
class KRScene;
class KRSceneManager : public KRResourceManager
{
public:
KRSceneManager(KRContext& context);
virtual ~KRSceneManager();
virtual KRResource* loadResource(const std::string& name, const std::string& extension, mimir::Block* data) override;
virtual KRResource* getResource(const std::string& name, const std::string& extension) override;
KRScene* createScene(const std::string& name);
void add(KRScene* scene);
KRScene* loadScene(const std::string& name, mimir::Block* data);
KRScene* getScene(const std::string& name);
KRScene* getFirstScene();
std::vector<std::string> getSceneNames();
unordered_map<std::string, KRScene*>& getScenes();
private:
unordered_map<std::string, KRScene*> m_scenes;
std::mutex m_mutex;
};

View File

@@ -0,0 +1,322 @@
//
// KRTexture.cpp
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#include "KREngine-common.h"
#include "KRTexture.h"
#include "block.h"
#include "KRContext.h"
#include "KRTextureManager.h"
KRTexture::KRTexture(KRContext& context, std::string name) : KRResource(context, name)
{
m_current_lod_max_dim = 0;
m_new_lod_max_dim = 0;
m_textureMemUsed = 0;
m_newTextureMemUsed = 0;
m_last_frame_used = 0;
m_last_frame_max_lod_coverage = 0.0f;
m_last_frame_usage = TEXTURE_USAGE_NONE;
m_handle_lock.clear();
m_haveNewHandles = false;
}
KRTexture::~KRTexture()
{
releaseHandles();
}
void KRTexture::TextureHandle::destroy(KRDeviceManager* deviceManager)
{
std::unique_ptr<KRDevice>& d = deviceManager->getDevice(device);
// TODO - Validate that device has not been lost
if (fullImageView != VK_NULL_HANDLE) {
vkDestroyImageView(d->m_logicalDevice, fullImageView, nullptr);
fullImageView = VK_NULL_HANDLE;
}
if (image != VK_NULL_HANDLE) {
VmaAllocator allocator = d->getAllocator();
vmaDestroyImage(allocator, image, allocation);
}
}
void KRTexture::destroyHandles()
{
KRDeviceManager* deviceManager = getContext().getDeviceManager();
for (TextureHandle t : m_handles) {
t.destroy(deviceManager);
}
m_handles.clear();
m_textureMemUsed = 0;
}
void KRTexture::destroyNewHandles()
{
KRDeviceManager* deviceManager = getContext().getDeviceManager();
for (TextureHandle t : m_newHandles) {
t.destroy(deviceManager);
}
m_newHandles.clear();
m_newTextureMemUsed = 0;
}
void KRTexture::releaseHandles()
{
long mem_size = getMemSize();
while (m_handle_lock.test_and_set()); // Spin lock
destroyNewHandles();
destroyHandles();
m_current_lod_max_dim = 0;
m_new_lod_max_dim = 0;
m_handle_lock.clear();
getContext().getTextureManager()->memoryChanged(-mem_size);
}
long KRTexture::getMemSize()
{
return m_textureMemUsed + m_newTextureMemUsed; // TODO - This is not 100% accurate, as loaded format may differ in size while in GPU memory
}
long KRTexture::getReferencedMemSize()
{
// Return the amount of memory used by other textures referenced by this texture (for cube maps and animated textures)
return 0;
}
void KRTexture::resize(int max_dim)
{
while (m_handle_lock.test_and_set()) {
}; // Spin lock
if (!m_haveNewHandles) {
if (max_dim > 0) {
int target_dim = max_dim;
if (target_dim < (int)m_min_lod_max_dim) target_dim = m_min_lod_max_dim;
if (m_new_lod_max_dim != target_dim || m_handles.empty()) {
assert(m_newTextureMemUsed == 0);
m_newTextureMemUsed = getMemRequiredForSize(target_dim);
getContext().getTextureManager()->memoryChanged(m_newTextureMemUsed);
getContext().getTextureManager()->addMemoryTransferredThisFrame(m_newTextureMemUsed);
if (createGPUTexture(target_dim)) {
m_new_lod_max_dim = target_dim;
} else {
getContext().getTextureManager()->memoryChanged(-m_newTextureMemUsed);
m_newTextureMemUsed = 0;
assert(false); // Failed to create the texture
}
}
}
}
m_handle_lock.clear();
}
void KRTexture::resetPoolExpiry(float lodCoverage, KRTexture::texture_usage_t textureUsage)
{
long current_frame = getContext().getCurrentFrame();
if (current_frame != m_last_frame_used) {
m_last_frame_used = current_frame;
m_last_frame_max_lod_coverage = 0.0f;
m_last_frame_usage = TEXTURE_USAGE_NONE;
getContext().getTextureManager()->primeTexture(this);
}
m_last_frame_max_lod_coverage = KRMAX(lodCoverage, m_last_frame_max_lod_coverage);
m_last_frame_usage = static_cast<texture_usage_t>(static_cast<int>(m_last_frame_usage) | static_cast<int>(textureUsage));
}
kraken_stream_level KRTexture::getStreamLevel(KRTexture::texture_usage_t textureUsage)
{
if (m_current_lod_max_dim == 0) {
return kraken_stream_level::STREAM_LEVEL_OUT;
} else if (m_current_lod_max_dim == KRMIN(getContext().KRENGINE_MAX_TEXTURE_DIM, (int)m_max_lod_max_dim)) {
return kraken_stream_level::STREAM_LEVEL_IN_HQ;
} else if (m_current_lod_max_dim >= KRMAX(getContext().KRENGINE_MIN_TEXTURE_DIM, (int)m_min_lod_max_dim)) {
return kraken_stream_level::STREAM_LEVEL_IN_LQ;
} else {
return kraken_stream_level::STREAM_LEVEL_OUT;
}
}
float KRTexture::getStreamPriority()
{
long current_frame = getContext().getCurrentFrame();
if (current_frame > m_last_frame_used + 5) {
return 1.0f - KRCLAMP((float)(current_frame - m_last_frame_used) / 60.0f, 0.0f, 1.0f);
} else {
float priority = 100.0f;
if (m_last_frame_usage & (TEXTURE_USAGE_UI | TEXTURE_USAGE_SHADOW_DEPTH)) {
priority += 10000000.0f;
}
if (m_last_frame_usage & (TEXTURE_USAGE_SKY_CUBE | TEXTURE_USAGE_PARTICLE | TEXTURE_USAGE_SPRITE | TEXTURE_USAGE_LIGHT_FLARE)) {
priority += 1000000.0f;
}
if (m_last_frame_usage & (TEXTURE_USAGE_DIFFUSE_MAP | TEXTURE_USAGE_AMBIENT_MAP | TEXTURE_USAGE_SPECULAR_MAP | TEXTURE_USAGE_NORMAL_MAP | TEXTURE_USAGE_REFLECTION_MAP)) {
priority += 100000.0f;
}
if (m_last_frame_usage & (TEXTURE_USAGE_LIGHT_MAP)) {
priority += 100000.0f;
}
if (m_last_frame_usage & (TEXTURE_USAGE_REFECTION_CUBE)) {
priority += 100000.0f;
}
priority += m_last_frame_max_lod_coverage * 10.0f;
return priority;
}
}
float KRTexture::getLastFrameLodCoverage() const
{
return m_last_frame_max_lod_coverage;
}
long KRTexture::getLastFrameUsed()
{
return m_last_frame_used;
}
bool KRTexture::isAnimated()
{
return false;
}
KRTexture* KRTexture::compress(bool premultiply_alpha)
{
return NULL;
}
int KRTexture::getCurrentLodMaxDim()
{
return m_current_lod_max_dim;
}
int KRTexture::getNewLodMaxDim()
{
return m_new_lod_max_dim;
}
int KRTexture::getMaxMipMap()
{
return m_max_lod_max_dim;
}
int KRTexture::getMinMipMap()
{
return m_min_lod_max_dim;
}
bool KRTexture::hasMipmaps()
{
return m_max_lod_max_dim != m_min_lod_max_dim;
}
void KRTexture::_swapHandles()
{
//while(m_handle_lock.test_and_set()); // Spin lock
if (!m_handle_lock.test_and_set()) {
if (m_haveNewHandles) {
destroyHandles();
m_handles.swap(m_newHandles);
m_textureMemUsed = (long)m_newTextureMemUsed;
m_newTextureMemUsed = 0;
m_current_lod_max_dim = m_new_lod_max_dim;
m_haveNewHandles = false;
}
m_handle_lock.clear();
}
}
VkImageView KRTexture::getFullImageView(KrDeviceHandle device)
{
for (TextureHandle& handle : m_handles) {
if (handle.device == device) {
return handle.fullImageView;
}
}
return VK_NULL_HANDLE;
}
VkImage KRTexture::getImage(KrDeviceHandle device)
{
for (TextureHandle& handle : m_handles) {
if (handle.device == device) {
return handle.image;
}
}
return VK_NULL_HANDLE;
}
bool KRTexture::allocate(KRDevice& device, hydra::Vector2i dimensions, VkImageCreateFlags imageCreateFlags, VkMemoryPropertyFlags properties, VkImage* image, VmaAllocation* allocation
#if KRENGINE_DEBUG_GPU_LABELS
, const char* debug_label
#endif
)
{
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent.width = static_cast<uint32_t>(dimensions.x);
imageInfo.extent.height = static_cast<uint32_t>(dimensions.y);
imageInfo.extent.depth = 1;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.flags = imageCreateFlags;
uint32_t queueFamilyIndices[2] = {};
imageInfo.pQueueFamilyIndices = queueFamilyIndices;
imageInfo.queueFamilyIndexCount = 0;
device.getQueueFamiliesForSharing(queueFamilyIndices, &imageInfo.queueFamilyIndexCount, &imageInfo.sharingMode);
VmaAllocationCreateInfo allocInfo = {};
allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
allocInfo.requiredFlags = properties;
VkResult res = vmaCreateImage(device.getAllocator(), &imageInfo, &allocInfo, image, allocation, nullptr);
if (res != VK_SUCCESS) {
return false;
}
#if KRENGINE_DEBUG_GPU_LABELS
device.setDebugLabel(*image, debug_label);
#endif
return true;
}

View File

@@ -0,0 +1,140 @@
//
// KRTexture.h
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#pragma once
#include "KREngine-common.h"
#include "KRContextObject.h"
#include "resources/KRResource.h"
namespace mimir {
class Block;
}
class KRCamera;
class KRDeviceManager;
class KRDevice;
class KRTexture : public KRResource
{
public:
KRTexture(KRContext& context, std::string name);
virtual ~KRTexture();
void releaseHandles();
long getMemSize();
virtual long getReferencedMemSize();
virtual long getMemRequiredForSize(int max_dim) = 0;
virtual void resize(int max_dim);
long getLastFrameUsed();
typedef enum
{
TEXTURE_USAGE_NONE = 0x00,
TEXTURE_USAGE_UI = 0x01,
TEXTURE_USAGE_SKY_CUBE = 0x02,
TEXTURE_USAGE_LIGHT_MAP = 0x04,
TEXTURE_USAGE_DIFFUSE_MAP = 0x08,
TEXTURE_USAGE_AMBIENT_MAP = 0x10,
TEXTURE_USAGE_SPECULAR_MAP = 0x20,
TEXTURE_USAGE_NORMAL_MAP = 0x40,
TEXTURE_USAGE_REFLECTION_MAP = 0x80,
TEXTURE_USAGE_REFECTION_CUBE = 0x100,
TEXTURE_USAGE_LIGHT_FLARE = 0x200,
TEXTURE_USAGE_SHADOW_DEPTH = 0x400,
TEXTURE_USAGE_PARTICLE = 0x800,
TEXTURE_USAGE_SPRITE = 0x1000
} texture_usage_t;
float getStreamPriority();
virtual void resetPoolExpiry(float lodCoverage, texture_usage_t textureUsage);
virtual bool isAnimated();
virtual KRTexture* compress(bool premultiply_alpha = false);
int getCurrentLodMaxDim();
int getNewLodMaxDim(); // For use by streamer only
int getMaxMipMap();
int getMinMipMap();
bool hasMipmaps();
virtual int getFaceCount() const = 0;
virtual VkFormat getFormat() const = 0;
kraken_stream_level getStreamLevel(KRTexture::texture_usage_t textureUsage);
float getLastFrameLodCoverage() const;
void _swapHandles();
VkImageView getFullImageView(KrDeviceHandle device);
VkImage getImage(KrDeviceHandle device);
protected:
virtual bool createGPUTexture(int lod_max_dim) = 0;
void destroyHandles();
void destroyNewHandles();
struct TextureHandle
{
VkImage image;
VkImageView fullImageView;
KrDeviceHandle device;
VmaAllocation allocation;
void destroy(KRDeviceManager* deviceManager);
};
std::vector<TextureHandle> m_handles;
std::vector<TextureHandle> m_newHandles;
std::atomic_bool m_haveNewHandles;
std::atomic_flag m_handle_lock;
int m_current_lod_max_dim;
int m_new_lod_max_dim;
uint32_t m_max_lod_max_dim;
uint32_t m_min_lod_max_dim;
long m_last_frame_used;
float m_last_frame_max_lod_coverage;
texture_usage_t m_last_frame_usage;
bool allocate(KRDevice& device, hydra::Vector2i dimensions, VkImageCreateFlags imageCreateFlags, VkMemoryPropertyFlags properties, VkImage* image, VmaAllocation* allocation
#if KRENGINE_DEBUG_GPU_LABELS
, const char* debug_label
#endif
);
private:
std::atomic<long> m_textureMemUsed;
std::atomic<long> m_newTextureMemUsed;
};

View File

@@ -0,0 +1,127 @@
//
// KRTexture2D.cpp
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#include "KREngine-common.h"
#include "KRTexture2D.h"
#include "KRTextureManager.h"
using namespace mimir;
KRTexture2D::KRTexture2D(KRContext& context, Block* data, std::string name) : KRTexture(context, name)
{
m_pData = data;
}
KRTexture2D::~KRTexture2D()
{
delete m_pData;
}
bool KRTexture2D::createGPUTexture(int lod_max_dim)
{
if (m_haveNewHandles) {
return true;
}
bool success = true;
int prev_lod_max_dim = m_new_lod_max_dim;
m_new_lod_max_dim = 0;
KRDeviceManager* deviceManager = getContext().getDeviceManager();
for (auto deviceItr = deviceManager->getDevices().begin(); deviceItr != deviceManager->getDevices().end(); deviceItr++) {
KRDevice& device = *(*deviceItr).second;
KrDeviceHandle deviceHandle = (*deviceItr).first;
VmaAllocator allocator = device.getAllocator();
KRTexture::TextureHandle& texture = m_newHandles.emplace_back();
texture.device = deviceHandle;
texture.allocation = VK_NULL_HANDLE;
texture.image = VK_NULL_HANDLE;
if (!allocate(device, getDimensions(), 0, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &texture.image, &texture.allocation
#if KRENGINE_DEBUG_GPU_LABELS
, getName().c_str()
#endif
)) {
success = false;
break;
}
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = texture.image;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = getFormat();
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
VkResult res = vkCreateImageView(device.m_logicalDevice, &viewInfo, nullptr, &texture.fullImageView);
if (res != VK_SUCCESS) {
success = false;
break;
}
if (!uploadTexture(device, texture.image, lod_max_dim, m_new_lod_max_dim)) {
success = false;
break;
}
}
if (success) {
m_new_lod_max_dim = prev_lod_max_dim;
m_haveNewHandles = true;
} else {
destroyNewHandles();
}
return success;
}
bool KRTexture2D::save(const std::string& path)
{
if (m_pData) {
return m_pData->save(path);
} else {
return false;
}
}
bool KRTexture2D::save(Block& data)
{
if (m_pData) {
data.append(*m_pData);
return true;
} else {
return false;
}
}

View File

@@ -0,0 +1,58 @@
//
// KRTexture2D.h
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#pragma once
#include "KREngine-common.h"
#include "block.h"
using std::list;
#include "KRTexture.h"
class KRDevice;
class KRTexture2D : public KRTexture
{
public:
KRTexture2D(KRContext& context, mimir::Block* data, std::string name);
virtual ~KRTexture2D();
virtual bool save(const std::string& path) override;
virtual bool save(mimir::Block& data) override;
virtual bool uploadTexture(KRDevice& device, VkImage& image, int lod_max_dim, int& current_lod_max_dim, bool premultiply_alpha = false) = 0;
virtual hydra::Vector2i getDimensions() const = 0;
protected:
mimir::Block* m_pData;
bool createGPUTexture(int lod_max_dim) override;
};

View File

@@ -0,0 +1,164 @@
//
// KRTextureAnimated.cpp
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#include "KREngine-common.h"
#include "KRTextureAnimated.h"
#include "KRTexture2D.h"
#include "KRContext.h"
KRTextureAnimated::KRTextureAnimated(KRContext& context, std::string name) : KRTexture(context, name)
{
// Format of name:
// animate:texturebasename,xx,yy
// Where - texturebasename is a prefix for the other textures
// - xx is the number of frames
// - yy is the framerate
// TODO - Add error handling for mal-formatted animated texture formats
size_t first_comma_pos = name.find(",");
size_t second_comma_pos = name.find(",", first_comma_pos + 1);
m_texture_base_name = name.substr(8, first_comma_pos - 8);
m_frame_count = atoi(name.substr(first_comma_pos + 1, second_comma_pos - first_comma_pos - 1).c_str());
m_frame_rate = (float)atof(name.substr(second_comma_pos + 1).c_str());
m_max_lod_max_dim = 2048;
m_min_lod_max_dim = 64;
for (int i = 0; i < m_frame_count; i++) {
KRTexture2D* frame_texture = textureForFrame(i);
if (frame_texture) {
if (frame_texture->getMaxMipMap() < (int)m_max_lod_max_dim) m_max_lod_max_dim = frame_texture->getMaxMipMap();
if (frame_texture->getMinMipMap() > (int)m_min_lod_max_dim) m_min_lod_max_dim = frame_texture->getMinMipMap();
}
}
}
std::string KRTextureAnimated::textureNameForFrame(int frame)
{
char szFrameNumber[10];
sprintf(szFrameNumber, "%i", frame);
return m_texture_base_name + szFrameNumber;
}
KRTexture2D* KRTextureAnimated::textureForFrame(int frame)
{
return (KRTexture2D*)getContext().getTextureManager()->getTexture(textureNameForFrame(frame));
}
KRTextureAnimated::~KRTextureAnimated()
{
}
bool KRTextureAnimated::createGPUTexture(int lod_max_dim)
{
return true;
}
long KRTextureAnimated::getMemRequiredForSize(int max_dim)
{
return 0; // Memory is allocated by individual frame textures
}
void KRTextureAnimated::resetPoolExpiry(float lodCoverage, texture_usage_t textureUsage)
{
KRTexture::resetPoolExpiry(lodCoverage, textureUsage);
for (int i = 0; i < m_frame_count; i++) {
KRTexture2D* frame_texture = textureForFrame(i);
if (frame_texture) {
frame_texture->resetPoolExpiry(lodCoverage, textureUsage); // Ensure that frames of animated textures do not expire from the texture pool prematurely, as they are referenced indirectly
}
}
}
// TODO - Vulkan refactoring. Perhaps The texture selection for animation should be done in the shader.
/*
void KRTextureAnimated::bind(int texture_unit)
{
resetPoolExpiry(0.0f, TEXTURE_USAGE_NONE); // TODO - Need to set parameters here for streaming priority?
KRTexture::bind(texture_unit);
int frame_number = (int)floor(fmodf(getContext().getAbsoluteTime() * m_frame_rate, (float)m_frame_count));
KRTexture2D* frame_texture = textureForFrame(frame_number);
if (frame_texture) {
frame_texture->bind(texture_unit);
}
}
*/
long KRTextureAnimated::getReferencedMemSize()
{
long referenced_mem = 0;
for (int i = 0; i < m_frame_count; i++) {
KRTexture2D* frame_texture = textureForFrame(i);
if (frame_texture) {
referenced_mem += frame_texture->getMemSize();
}
}
return referenced_mem;
}
bool KRTextureAnimated::isAnimated()
{
return true;
}
std::string KRTextureAnimated::getExtension()
{
return ""; // Animated textures are just references; there are no files to output
}
bool KRTextureAnimated::save(const std::string& path)
{
return true; // Animated textures are just references; there are no files to output
}
bool KRTextureAnimated::save(Block& data)
{
return true; // Animated textures are just references; there are no files to output
}
void KRTextureAnimated::resize(int max_dim)
{
// Purposely not calling the superclass method
}
int KRTextureAnimated::getFaceCount() const
{
return 1;
}
VkFormat KRTextureAnimated::getFormat() const
{
return VK_FORMAT_UNDEFINED;
}

View File

@@ -0,0 +1,67 @@
//
// KRTextureAnimated.h
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#pragma once
#include "KRTexture.h"
#include "KRTexture2D.h"
using namespace mimir;
class KRTextureAnimated : public KRTexture
{
public:
KRTextureAnimated(KRContext& context, std::string name);
virtual ~KRTextureAnimated();
virtual std::string getExtension() override;
virtual bool save(const std::string& path) override;
virtual bool save(Block& data) override;
virtual long getMemRequiredForSize(int max_dim) override;
virtual void resetPoolExpiry(float lodCoverage, texture_usage_t textureUsage) override;
virtual long getReferencedMemSize() override;
virtual bool isAnimated() override;
virtual void resize(int max_dim) override;
virtual int getFaceCount() const override;
virtual VkFormat getFormat() const override;
private:
bool createGPUTexture(int lod_max_dim) override;
float m_frame_rate;
int m_frame_count;
std::string m_texture_base_name;
std::string textureNameForFrame(int frame);
KRTexture2D* textureForFrame(int frame);
};

View File

@@ -0,0 +1,183 @@
//
// KRTextureCube.cpp
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#include "KRTextureCube.h"
#include "KRTexture2D.h"
#include "KRContext.h"
using namespace mimir;
using namespace hydra;
KRTextureCube::KRTextureCube(KRContext& context, std::string name) : KRTexture(context, name)
{
m_max_lod_max_dim = 2048;
m_min_lod_max_dim = 64;
for (int i = 0; i < 6; i++) {
m_textures[i] = NULL;
std::string faceName = getName() + SUFFIXES[i];
m_textures[i] = (KRTexture2D*)getContext().getTextureManager()->getTexture(faceName);
if (m_textures[i]) {
if (m_textures[i]->getMaxMipMap() < (int)m_max_lod_max_dim) m_max_lod_max_dim = m_textures[i]->getMaxMipMap();
if (m_textures[i]->getMinMipMap() > (int)m_min_lod_max_dim) m_min_lod_max_dim = m_textures[i]->getMinMipMap();
} else {
assert(false);
}
}
}
KRTextureCube::~KRTextureCube()
{}
bool KRTextureCube::createGPUTexture(int lod_max_dim)
{
assert(!m_haveNewHandles); // Only allow one resize per frame
bool success = true;
int prev_lod_max_dim = m_new_lod_max_dim;
m_new_lod_max_dim = 0;
bool bMipMaps = false;
Vector2i dimensions = Vector2i::Zero();
for (int i = 0; i < 6; i++) {
if (!m_textures[i]) {
success = false;
} else {
KRTexture2D& tex = *m_textures[i];
Vector2i texDimensions = tex.getDimensions();
if (dimensions.x == 0) {
dimensions = texDimensions;
} else if (dimensions != texDimensions) {
success = false;
}
if (tex.hasMipmaps()) {
bMipMaps = true;
}
}
}
if (!success) {
// Not all face images were loaded, or they have
// mismatched dimensions
// TODO - Perhaps we should have multiple error result codes.
return false;
}
KRDeviceManager* deviceManager = getContext().getDeviceManager();
for (auto deviceItr = deviceManager->getDevices().begin(); deviceItr != deviceManager->getDevices().end(); deviceItr++) {
KRDevice& device = *(*deviceItr).second;
KrDeviceHandle deviceHandle = (*deviceItr).first;
VmaAllocator allocator = device.getAllocator();
KRTexture::TextureHandle& texture = m_newHandles.emplace_back();
texture.device = deviceHandle;
texture.allocation = VK_NULL_HANDLE;
texture.image = VK_NULL_HANDLE;
if (!allocate(device, dimensions, VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &texture.image, &texture.allocation
#if KRENGINE_DEBUG_GPU_LABELS
, getName().c_str()
#endif
)) {
success = false;
break;
}
for (int i = 0; i < 6; i++) {
std::string faceName = getName() + SUFFIXES[i];
if (m_textures[i]) {
// TODO - Vulkan refactoring. We need to create a cube map texture rather than individual 2d textures.
m_textures[i]->uploadTexture(device, texture.image, lod_max_dim, m_new_lod_max_dim);
}
}
}
if (success) {
m_haveNewHandles = true;
} else {
destroyHandles();
m_new_lod_max_dim = prev_lod_max_dim;
}
return success;
}
long KRTextureCube::getMemRequiredForSize(int max_dim)
{
int target_dim = max_dim;
if (target_dim < (int)m_min_lod_max_dim) target_dim = m_min_lod_max_dim;
long memoryRequired = 0;
for (int i = 0; i < 6; i++) {
if (m_textures[i]) {
memoryRequired += m_textures[i]->getMemRequiredForSize(target_dim);
}
}
return memoryRequired;
}
void KRTextureCube::resetPoolExpiry(float lodCoverage, texture_usage_t textureUsage)
{
KRTexture::resetPoolExpiry(lodCoverage, textureUsage);
for(int i=0; i<6; i++) {
if(m_textures[i]) {
m_textures[i]->resetPoolExpiry(lodCoverage, textureUsage); // Ensure that side of cube maps do not expire from the texture pool prematurely, as they are referenced indirectly
}
}
}
std::string KRTextureCube::getExtension()
{
return ""; // Cube maps are just references; there are no files to output
}
bool KRTextureCube::save(const std::string& path)
{
return true; // Cube maps are just references; there are no files to output
}
bool KRTextureCube::save(Block& data)
{
return true; // Cube maps are just references; there are no files to output
}
int KRTextureCube::getFaceCount() const
{
return 6;
}
VkFormat KRTextureCube::getFormat() const
{
// TODO - Implement
return VK_FORMAT_UNDEFINED;
}

View File

@@ -0,0 +1,65 @@
//
// KRTextureCube.h
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#pragma once
#include "KRTexture.h"
class KRTexture2D;
class KRTextureCube : public KRTexture
{
public:
KRTextureCube(KRContext& context, std::string name);
virtual ~KRTextureCube();
virtual std::string getExtension() override;
virtual bool save(const std::string& path) override;
virtual bool save(mimir::Block& data) override;
virtual long getMemRequiredForSize(int max_dim) override;
virtual void resetPoolExpiry(float lodCoverage, texture_usage_t textureUsage) override;
virtual int getFaceCount() const override;
virtual VkFormat getFormat() const override;
private:
bool createGPUTexture(int lod_max_dim) override;
const char* SUFFIXES[6] = {
"_positive_x",
"_negative_x",
"_positive_y",
"_negative_y",
"_positive_z",
"_negative_z"
};
KRTexture2D* m_textures[6];
};

View File

@@ -0,0 +1,466 @@
//
// KRTextureKTX.cpp
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#include "KRTextureKTX.h"
#include "KRTextureManager.h"
#include "KREngine-common.h"
using namespace hydra;
__uint8_t _KTXFileIdentifier[12] = {
0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A
};
KRTextureKTX::KRTextureKTX(KRContext& context, Block* data, std::string name) : KRTexture2D(context, data, name)
{
m_pData->copy(&m_header, 0, sizeof(KTXHeader));
if (memcmp(_KTXFileIdentifier, m_header.identifier, 12) != 0) {
assert(false); // Header not recognized
}
if (m_header.endianness != 0x04030201) {
assert(false); // Endianness not (yet) supported
}
if (m_header.pixelDepth != 0) {
assert(false); // 3d textures not (yet) supported
}
if (m_header.numberOfArrayElements != 0) {
assert(false); // Array textures not (yet) supported
}
if (m_header.numberOfFaces != 1) {
assert(false); // Cube-map textures are only supported as a file for each separate face (for now)
}
uint32_t blockStart = sizeof(KTXHeader) + m_header.bytesOfKeyValueData;
uint32_t width = m_header.pixelWidth, height = m_header.pixelHeight;
for (int mipmap_level = 0; mipmap_level < (int)KRMAX(m_header.numberOfMipmapLevels, 1); mipmap_level++) {
uint32_t blockLength;
data->copy(&blockLength, blockStart, 4);
blockStart += 4;
m_blocks.push_back(m_pData->getSubBlock(blockStart, blockLength));
blockStart += blockLength;
blockStart = KRALIGN(blockStart);
width = width >> 1;
if (width < 1) {
width = 1;
}
height = height >> 1;
if (height < 1) {
height = 1;
}
}
m_max_lod_max_dim = KRMAX(m_header.pixelWidth, m_header.pixelHeight);
m_min_lod_max_dim = KRMAX(width, height);
}
KRTextureKTX::KRTextureKTX(KRContext& context, std::string name, unsigned int internal_format, unsigned int base_internal_format, int width, int height, const std::list<Block*>& blocks) : KRTexture2D(context, new Block(), name)
{
memcpy(m_header.identifier, _KTXFileIdentifier, 12);
m_header.endianness = 0x04030201;
m_header.glType = 0;
m_header.glTypeSize = 1;
m_header.glFormat = 0;
m_header.glInternalFormat = internal_format;
m_header.glBaseInternalFormat = base_internal_format;
m_header.pixelWidth = width;
m_header.pixelHeight = height;
m_header.pixelDepth = 0;
m_header.numberOfArrayElements = 0;
m_header.numberOfFaces = 1;
m_header.numberOfMipmapLevels = (__uint32_t)blocks.size();
m_header.bytesOfKeyValueData = 0;
m_pData->append(&m_header, sizeof(m_header));
for (auto block_itr = blocks.begin(); block_itr != blocks.end(); block_itr++) {
Block* source_block = *block_itr;
__uint32_t block_size = (__uint32_t)source_block->getSize();
m_pData->append(&block_size, 4);
m_pData->append(*source_block);
m_blocks.push_back(m_pData->getSubBlock((int)m_pData->getSize() - (int)block_size, (int)block_size));
size_t alignment_padding_size = KRALIGN(m_pData->getSize()) - m_pData->getSize();
__uint8_t alignment_padding[4] = { 0, 0, 0, 0 };
if (alignment_padding_size > 0) {
m_pData->append(&alignment_padding, alignment_padding_size);
}
}
}
KRTextureKTX::~KRTextureKTX()
{
for (std::list<Block*>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) {
Block* block = *itr;
delete block;
}
m_blocks.clear();
}
Vector2i KRTextureKTX::getDimensions() const
{
return hydra::Vector2i::Create(Vector2i::Create(m_header.pixelWidth, m_header.pixelHeight));
}
int KRTextureKTX::getFaceCount() const
{
return m_header.numberOfFaces;
}
VkFormat KRTextureKTX::getFormat() const
{
if (m_header.glFormat != 0) {
// Non-Compressed formats, from table 8.3 of OpenGL 4.4 spec:
switch (m_header.glFormat) {
case 0x1901: // GL_STENCIL_INDEX
case 0x1902: // GL_DEPTH_COMPONENT
case 0x84F9: // GL_DEPTH_STENCIL
return VK_FORMAT_UNDEFINED;
case 0x1903: // GL_RED
case 0x1904: // GL_GREEN
case 0x1905: // GL_BLUE
case 0x8D94: // GL_RED_INTEGER
case 0x8D95: // GL_GREEN_INTEGER
case 0x8D96: // GL_BLUE_INTEGER
// Types applicable to a single component from table 8.2 of OpenGL 4.4 spec
switch (m_header.glType) {
case 0x1401: // UNSIGNED_BYTE
return VK_FORMAT_R8_UNORM;
case 0x1400: // BYTE
return VK_FORMAT_R8_SNORM;
case 0x1403: // UNSIGNED_SHORT
return VK_FORMAT_R16_UNORM;
case 0x1402: // SHORT
return VK_FORMAT_R16_SNORM;
case 0x1405: // UNSIGNED_INT
return VK_FORMAT_R32_UINT;
case 0x1404: // INT
return VK_FORMAT_R32_SINT;
case 0x140B: // HALF_FLOAT
return VK_FORMAT_R16_SFLOAT;
case 0x1406: // FLOAT
return VK_FORMAT_R32_SFLOAT;
default:
return VK_FORMAT_UNDEFINED;
}
case 0x8227: // GL_RG
case 0x8228: // GL_RG_INTEGER
// Types applicable to two components from table 8.2 of OpenGL 4.4 spec
switch (m_header.glType) {
case 0x1401: // UNSIGNED_BYTE
return VK_FORMAT_R8G8_UNORM;
case 0x1400: // BYTE
return VK_FORMAT_R8G8_SNORM;
case 0x1403: // UNSIGNED_SHORT
return VK_FORMAT_R16G16_UNORM;
case 0x1402: // SHORT
return VK_FORMAT_R16G16_SNORM;
case 0x1405: // UNSIGNED_INT
return VK_FORMAT_R32G32_UINT;
case 0x1404: // INT
return VK_FORMAT_R32G32_SINT;
case 0x140B: // HALF_FLOAT
return VK_FORMAT_R16G16_SFLOAT;
case 0x1406: // FLOAT
return VK_FORMAT_R32G32_SFLOAT;
default:
return VK_FORMAT_UNDEFINED;
}
case 0x1907: // GL_RGB
case 0x80E0: // GL_BGR
case 0x8D98: // GL_RGB_INTEGER
case 0x8D9A: // GL_BGR_INTEGER
// Types applicable to three components from table 8.2 of OpenGL 4.4 spec
switch (m_header.glType) {
case 0x1401: // UNSIGNED_BYTE
return VK_FORMAT_R8G8B8_UNORM;
case 0x1400: // BYTE
return VK_FORMAT_R8G8B8_SNORM;
case 0x1403: // UNSIGNED_SHORT
return VK_FORMAT_R16G16B16_UNORM;
case 0x1402: // SHORT
return VK_FORMAT_R16G16B16_SNORM;
case 0x1405: // UNSIGNED_INT
return VK_FORMAT_R32G32B32_UINT;
case 0x1404: // INT
return VK_FORMAT_R32G32B32_SINT;
case 0x140B: // HALF_FLOAT
return VK_FORMAT_R16G16B16_SFLOAT;
case 0x1406: // FLOAT
return VK_FORMAT_R32G32B32_SFLOAT;
// UNSIGNED_BYTE_3_3_2 not supported
// UNSIGNED_BYTE_2_3_3_REV not supported
case 0x8363: // UNSIGNED_SHORT_5_6_5 ushort
return VK_FORMAT_R5G6B5_UNORM_PACK16;
case 0x8364: // UNSIGNED_SHORT_5_6_5_REV
return VK_FORMAT_B5G6R5_UNORM_PACK16;
case 0x8C3B: // UNSIGNED_INT_10F_11F_11F_REV
return VK_FORMAT_B10G11R11_UFLOAT_PACK32;
default:
return VK_FORMAT_UNDEFINED;
}
case 0x1908: // GL_RGBA
case 0x80E1: // GL_BGRA
case 0x8D99: // GL_RGBA_INTEGER
case 0x8D9B: // GL_BGRA_INTEGER
// Types applicable to three components from table 8.2 of OpenGL 4.4 spec
switch (m_header.glType) {
case 0x1401: // UNSIGNED_BYTE
return VK_FORMAT_R8G8B8A8_UNORM;
case 0x1400: // BYTE
return VK_FORMAT_R8G8B8A8_SNORM;
case 0x1403: // UNSIGNED_SHORT
return VK_FORMAT_R16G16B16A16_UNORM;
case 0x1402: // SHORT
return VK_FORMAT_R16G16B16A16_SNORM;
case 0x1405: // UNSIGNED_INT
return VK_FORMAT_R32G32B32A32_UINT;
case 0x1404: // INT
return VK_FORMAT_R32G32B32A32_SINT;
case 0x140B: // HALF_FLOAT
return VK_FORMAT_R16G16B16A16_SFLOAT;
case 0x1406: // FLOAT
return VK_FORMAT_R32G32B32A32_SFLOAT;
case 0x8033: // UNSIGNED_SHORT_4_4_4_4
case 0x8365: // UNSIGNED_SHORT_4_4_4_4_REV
return VK_FORMAT_R4G4B4A4_UNORM_PACK16;
case 0x8034: // UNSIGNED_SHORT_5_5_5_1
return VK_FORMAT_R5G5B5A1_UNORM_PACK16;
case 0x8366: // UNSIGNED_SHORT_1_5_5_5_REV
return VK_FORMAT_A1R5G5B5_UNORM_PACK16;
case 0x8035: // UNSIGNED_INT_8_8_8_8 uint
return VK_FORMAT_R8G8B8A8_UINT;
case 0x8367: // UNSIGNED_INT_8_8_8_8_REV
return VK_FORMAT_A8B8G8R8_UINT_PACK32;
// UNSIGNED_INT_10_10_10_2 not supported
case 0x8368: // UNSIGNED_INT_2_10_10_10_REV
return VK_FORMAT_A2R10G10B10_UINT_PACK32;
// UNSIGNED_INT_5_9_9_9_REV not supported
default:
return VK_FORMAT_UNDEFINED;
}
default:
return VK_FORMAT_UNDEFINED;
}
}
/*
// Internal format for uncompressed textures
switch (m_header.glInternalFormat) {
// Sized internal color formats, from table 8.12 of OpenGL 4.4 spec
case 0x823A: // RG16UI RG ui16 ui16
case 0x823B: // RG32I RG i32 i32
case 0x823C: // RG32UI RG ui32 ui32
case 0x8D8F: // RGB8I RGB i8 i8 i8
case 0x8D7D: // RGB8UI RGB ui8 ui8 ui8
case 0x8D89: // RGB16I RGB i16 i16 i16
case 0x8D77: // RGB16UI RGB ui16 ui16 ui16
case 0x8D83: // RGB32I RGB i32 i32 i32
case 0x8D71: // RGB32UI RGB ui32 ui32 ui32
case 0x8D8E: // RGBA8I RGBA i8 i8 i8 i8
case 0x8D7C: // RGBA8UI RGBA ui8 ui8 ui8 ui8
case 0x8D88: // RGBA16I RGBA i16 i16 i16 i16
case 0x8D76: // RGBA16UI RGBA ui16 ui16 ui16 ui16
case 0x8D82: // RGBA32I RGBA i32 i32 i32 i32
case 0x8D70: // RGBA32UI RGBA ui32 ui32 ui32 ui32
// Sized internal depth and stencil formats, from table 8.13 of OpenGL 4.4 spec
case 0x81A5: // DEPTH_COMPONENT16 DEPTH_COMPONENT 16
case 0x81A6: // DEPTH_COMPONENT24 DEPTH_COMPONENT 24
case 0x81A7: // DEPTH_COMPONENT32 DEPTH_COMPONENT 32
case 0x8CAC: // DEPTH_COMPONENT32F DEPTH_COMPONENT f32
case 0x88F0: // DEPTH24_STENCIL8 DEPTH_STENCIL 24 ui8
case 0x8CAD: // DEPTH32F_STENCIL8 DEPTH_STENCIL f32 ui8
case 0x8D46: // STENCIL_INDEX1 STENCIL_INDEX ui1
case 0x8D47: // STENCIL_INDEX4 STENCIL_INDEX ui4
case 0x8D48: // STENCIL_INDEX8 STENCIL_INDEX ui8
case 0x8D49: // STENCIL_INDEX16 STENCIL_INDEX ui16
}
*/
// Internal format for compressed textures
switch (m_header.glInternalFormat) {
// Compressed formats, from table 8.14 of OpenGL 4.4 spec:
case 0x8225: // COMPRESSED_RED RED Generic unorm
case 0x8226: // COMPRESSED_RG RG Generic unorm
case 0x84ED: // COMPRESSED_RGB RGB Generic unorm
case 0x84EE: // COMPRESSED_RGBA RGBA Generic unorm
case 0x8C48: // COMPRESSED_SRGB RGB Generic unorm
case 0x8C49: // COMPRESSED_SRGB_ALPHA RGBA Generic unorm
case 0x8DBB: // COMPRESSED_RED_RGTC1 RED Specific unorm
// Generic compressed formats not supported
return VK_FORMAT_UNDEFINED;
break;
case 0x8DBC: // COMPRESSED_SIGNED_RED_RGTC1 RED Specific snorm
return VK_FORMAT_BC4_SNORM_BLOCK;
case 0x8DBD: // COMPRESSED_RG_RGTC2 RG Specific unorm
return VK_FORMAT_BC5_UNORM_BLOCK;
case 0x8DBE: // COMPRESSED_SIGNED_RG_RGTC2 RG Specific snorm
return VK_FORMAT_BC5_SNORM_BLOCK;
case 0x8E8C: // COMPRESSED_RGBA_BPTC_UNORM RGBA Specific unorm
return VK_FORMAT_BC7_UNORM_BLOCK;
case 0x8E8D: // COMPRESSED_SRGB_ALPHA_BPTC_UNORM RGBA Specific unorm
return VK_FORMAT_BC7_SRGB_BLOCK;
case 0x8E8E: // COMPRESSED_RGB_BPTC_SIGNED_FLOAT RGB Specific float
return VK_FORMAT_BC6H_SFLOAT_BLOCK;
case 0x8E8F: // COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT RGB Specific float
return VK_FORMAT_BC6H_UFLOAT_BLOCK;
case 0x9274: // COMPRESSED_RGB8_ETC2 RGB Specific unorm
return VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
case 0x9275: // COMPRESSED_SRGB8_ETC2 RGB Specific unorm
return VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK;
case 0x9276: // COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 RGB Specific unorm
return VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK;
case 0x9277: // COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 RGB Specific unorm
return VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK;
case 0x9278: // COMPRESSED_RGBA8_ETC2_EAC RGBA Specific unorm
return VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
case 0x9279: // COMPRESSED_SRGB8_ALPHA8_ETC2_EAC RGBA Specific unorm
return VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK;
case 0x9270: // COMPRESSED_R11_EAC RED Specific unorm
return VK_FORMAT_EAC_R11_UNORM_BLOCK;
case 0x9271: // COMPRESSED_SIGNED_R11_EAC RED Specific snorm
return VK_FORMAT_EAC_R11_SNORM_BLOCK;
case 0x9272: // COMPRESSED_RG11_EAC RG Specific unorm
return VK_FORMAT_EAC_R11G11_UNORM_BLOCK;
case 0x9273: // COMPRESSED_SIGNED_RG11_EAC RG Specific snorm
return VK_FORMAT_EAC_R11G11_SNORM_BLOCK;
default:
return VK_FORMAT_UNDEFINED;
}
}
long KRTextureKTX::getMemRequiredForSize(int max_dim)
{
int target_dim = max_dim;
if (target_dim < (int)m_min_lod_max_dim) target_dim = target_dim;
// Determine how much memory will be consumed
int width = m_header.pixelWidth;
int height = m_header.pixelHeight;
long memoryRequired = 0;
for (std::list<Block*>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) {
Block* block = *itr;
if (width <= target_dim && height <= target_dim) {
memoryRequired += (long)block->getSize();
}
width = width >> 1;
if (width < 1) {
width = 1;
}
height = height >> 1;
if (height < 1) {
height = 1;
}
}
return memoryRequired;
}
bool KRTextureKTX::uploadTexture(KRDevice& device, VkImage& image, int lod_max_dim, int& current_lod_max_dim, bool premultiply_alpha)
{
int target_dim = lod_max_dim;
if (target_dim < (int)m_min_lod_max_dim) target_dim = m_min_lod_max_dim;
if (m_blocks.size() == 0) {
return false;
}
// Determine how much memory will be consumed
int width = m_header.pixelWidth;
int height = m_header.pixelHeight;
long memoryRequired = 0;
long memoryTransferred = 0;
// Upload texture data
int destination_level = 0;
int source_level = 0;
for (std::list<Block*>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) {
Block* block = *itr;
if (width <= target_dim && height <= target_dim) {
if (width > current_lod_max_dim) {
current_lod_max_dim = width;
}
if (height > current_lod_max_dim) {
current_lod_max_dim = height;
}
block->lock();
/*
* TODO - Vulkan Refactoring
GLDEBUG(glCompressedTexImage2D(target, destination_level, (unsigned int)m_header.glInternalFormat, width, height, 0, (int)block->getSize(), block->getStart()));
*/
block->unlock();
memoryTransferred += (long)block->getSize(); // memoryTransferred does not include throughput of mipmap levels copied through glCopyTextureLevelsAPPLE
memoryRequired += (long)block->getSize();
//
// err = glGetError();
// if (err != GL_NO_ERROR) {
// assert(false);
// return false;
// }
//
destination_level++;
}
if (width <= m_current_lod_max_dim && height <= m_current_lod_max_dim) {
source_level++;
}
width = width >> 1;
if (width < 1) {
width = 1;
}
height = height >> 1;
if (height < 1) {
height = 1;
}
}
return true;
}
std::string KRTextureKTX::getExtension()
{
return "ktx";
}

View File

@@ -0,0 +1,76 @@
//
// KRTextureKTX.h
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#pragma once
#include "KRTexture2D.h"
using namespace mimir;
class KRTextureKTX : public KRTexture2D
{
public:
KRTextureKTX(KRContext& context, Block* data, std::string name);
KRTextureKTX(KRContext& context, std::string name, unsigned int internal_format, unsigned int base_internal_format, int width, int height, const std::list<Block*>& blocks);
virtual ~KRTextureKTX();
virtual std::string getExtension() override;
bool uploadTexture(KRDevice& device, VkImage& image, int lod_max_dim, int& current_lod_max_dim, bool premultiply_alpha = false) override;
virtual long getMemRequiredForSize(int max_dim) override;
virtual hydra::Vector2i getDimensions() const override;
virtual int getFaceCount() const override;
virtual VkFormat getFormat() const override;
protected:
std::list<Block*> m_blocks;
typedef struct _KTXHeader
{
__uint8_t identifier[12];
__uint32_t endianness;
__uint32_t glType;
__uint32_t glTypeSize;
__uint32_t glFormat;
__uint32_t glInternalFormat;
__uint32_t glBaseInternalFormat;
__uint32_t pixelWidth;
__uint32_t pixelHeight;
__uint32_t pixelDepth;
__uint32_t numberOfArrayElements;
__uint32_t numberOfFaces;
__uint32_t numberOfMipmapLevels;
__uint32_t bytesOfKeyValueData;
} KTXHeader;
KTXHeader m_header;
};

View File

@@ -0,0 +1,196 @@
//
// KRTextureKTX2.cpp
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#include "KRTextureKTX2.h"
#include "KRTextureManager.h"
#include "KREngine-common.h"
using namespace mimir;
using namespace hydra;
__uint8_t _KTX2FileIdentifier[12] = {
0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A
};
KRTextureKTX2::KRTextureKTX2(KRContext& context, Block* data, std::string name) : KRTexture2D(context, data, name)
{
m_pData->copy(&m_header, 0, sizeof(KTX2Header));
if (memcmp(_KTX2FileIdentifier, m_header.identifier, 12) != 0) {
assert(false); // Header not recognized
}
if (m_header.pixelDepth != 0) {
assert(false); // 3d textures not (yet) supported
}
if (m_header.layerCount != 0) {
assert(false); // Array textures not (yet) supported
}
if (m_header.faceCount != 1 && m_header.faceCount != 6) {
assert(false);
}
if (m_header.supercompressionScheme != 0) {
assert(false); // Not yet supported
}
uint32_t width = m_header.pixelWidth >> m_header.levelCount;
uint32_t height = m_header.pixelHeight >> m_header.levelCount;
if (width < 1) {
width = 1;
}
if (height < 1) {
height = 1;
}
m_max_lod_max_dim = KRMAX(m_header.pixelWidth, m_header.pixelHeight);
m_min_lod_max_dim = KRMAX(width, height);
}
KRTextureKTX2::~KRTextureKTX2()
{
}
Vector2i KRTextureKTX2::getDimensions() const
{
return Vector2i::Create(Vector2i::Create(m_header.pixelWidth, m_header.pixelHeight));
}
long KRTextureKTX2::getMemRequiredForSize(int max_dim)
{
int target_dim = max_dim;
if (target_dim < (int)m_min_lod_max_dim) target_dim = target_dim;
// Determine how much memory will be consumed
int width = m_header.pixelWidth;
int height = m_header.pixelHeight;
long memoryRequired = 0;
for (__uint32_t level = 0; level < m_header.levelCount; level++) {
KTX2LevelIndex levelIndex;
m_pData->copy(&levelIndex, sizeof(m_header) + sizeof(KTX2LevelIndex) * level, sizeof(KTX2LevelIndex));
if (width <= target_dim && height <= target_dim) {
memoryRequired += (long)levelIndex.byteLength;
}
width = width >> 1;
if (width < 1) {
width = 1;
}
height = height >> 1;
if (height < 1) {
height = 1;
}
}
return memoryRequired;
}
bool KRTextureKTX2::uploadTexture(KRDevice& device, VkImage& image, int lod_max_dim, int& current_lod_max_dim, bool premultiply_alpha)
{
int target_dim = lod_max_dim;
if (target_dim < (int)m_min_lod_max_dim) target_dim = m_min_lod_max_dim;
// Determine how much memory will be consumed
int width = m_header.pixelWidth;
int height = m_header.pixelHeight;
long memoryRequired = 0;
long memoryTransferred = 0;
// Upload texture data
int destination_level = 0;
int source_level = 0;
for (__uint32_t level = 0; level < m_header.levelCount; level++) {
KTX2LevelIndex levelIndex;
m_pData->copy(&levelIndex, sizeof(m_header) + sizeof(KTX2LevelIndex) * level, sizeof(KTX2LevelIndex));
if (width <= target_dim && height <= target_dim) {
if (width > current_lod_max_dim) {
current_lod_max_dim = width;
}
if (height > current_lod_max_dim) {
current_lod_max_dim = height;
}
/*
* TODO - Vulkan Refactoring
GLDEBUG(glCompressedTexImage2D(target, destination_level, (unsigned int)m_header.glInternalFormat, width, height, 0, (int)block->getSize(), block->getStart()));
*/
memoryTransferred += (long)levelIndex.byteLength; // memoryTransferred does not include throughput of mipmap levels copied through glCopyTextureLevelsAPPLE
memoryRequired += (long)levelIndex.byteLength;
//
// err = glGetError();
// if (err != GL_NO_ERROR) {
// assert(false);
// return false;
// }
//
destination_level++;
}
if (width <= m_current_lod_max_dim && height <= m_current_lod_max_dim) {
source_level++;
}
width = width >> 1;
if (width < 1) {
width = 1;
}
height = height >> 1;
if (height < 1) {
height = 1;
}
}
return true;
}
std::string KRTextureKTX2::getExtension()
{
return "ktx2";
}
int KRTextureKTX2::getFaceCount() const
{
return m_header.faceCount;
}
VkFormat KRTextureKTX2::getFormat() const
{
// TODO - Implement
return VK_FORMAT_UNDEFINED;
}

View File

@@ -0,0 +1,81 @@
//
// KRTextureKTX2.h
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#pragma once
#include "KRTexture2D.h"
class KRTextureKTX2 : public KRTexture2D
{
public:
KRTextureKTX2(KRContext& context, mimir::Block* data, std::string name);
virtual ~KRTextureKTX2();
virtual std::string getExtension() override;
bool uploadTexture(KRDevice& device, VkImage& image, int lod_max_dim, int& current_lod_max_dim, bool premultiply_alpha = false) override;
virtual long getMemRequiredForSize(int max_dim) override;
virtual hydra::Vector2i getDimensions() const override;
virtual int getFaceCount() const override;
virtual VkFormat getFormat() const override;
protected:
typedef struct
{
__uint8_t identifier[12];
__uint32_t vkFormat;
__uint32_t typeSize;
__uint32_t pixelWidth;
__uint32_t pixelHeight;
__uint32_t pixelDepth;
__uint32_t layerCount;
__uint32_t faceCount;
__uint32_t levelCount;
__uint32_t supercompressionScheme;
// Index
__uint32_t dfdByteOffset;
__uint32_t dfdByteLength;
__uint32_t kvdByteOffset;
__uint32_t kvdByteLength;
__uint32_t sgdByteOffset;
__uint32_t sgdByteLength;
} KTX2Header;
typedef struct
{
__uint64_t byteOffset;
__uint64_t byteLength;
__uint64_t uncompressedByteLength;
} KTX2LevelIndex;
KTX2Header m_header;
};

View File

@@ -0,0 +1,430 @@
//
// KRTextureManager.cpp
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#include "KREngine-common.h"
#include "KRTextureManager.h"
#include "KRContext.h"
#include "KRTexture2D.h"
#include "KRTexturePVR.h"
#include "KRTextureTGA.h"
#include "KRTextureKTX.h"
#include "KRTextureKTX2.h"
#include "KRTextureCube.h"
#include "KRTextureAnimated.h"
#include "KRContext.h"
KRTextureManager::KRTextureManager(KRContext& context) : KRResourceManager(context)
{
m_textureMemUsed = 0;
m_memoryTransferredThisFrame = 0;
m_streamerComplete = true;
}
void KRTextureManager::destroy()
{
for (unordered_map<std::string, KRTexture*>::iterator itr = m_textures.begin(); itr != m_textures.end(); ++itr) {
delete (*itr).second;
}
m_textures.clear();
}
KRTextureManager::~KRTextureManager()
{
// Must call destroy() first
assert(m_textures.empty());
}
void KRTextureManager::setMaxAnisotropy(float max_anisotropy)
{
m_maxAnisotropy = max_anisotropy;
}
KRResource* KRTextureManager::loadResource(const std::string& name, const std::string& extension, Block* data)
{
if (extension.compare("pvr") == 0 ||
extension.compare("ktx") == 0 ||
extension.compare("ktx2") == 0 ||
extension.compare("tga") == 0) {
return loadTexture(name.c_str(), extension.c_str(), data);
}
return nullptr;
}
KRResource* KRTextureManager::getResource(const std::string& name, const std::string& extension)
{
if (extension.compare("pvr") == 0 ||
extension.compare("ktx") == 0 ||
extension.compare("ktx2") == 0 ||
extension.compare("tga") == 0) {
// TODO - Currently textures must have a unique name, without consideration
// of extensions. When textures are compressed, the uncompressed versions
// are removed. Both compressed and un-compressed textures should co-exist
// with the renderer prioritizing as necessary. This will facilitate more
// ergonomic usage within toolchain and editor GUI.
return getTexture(name);
}
return nullptr;
}
KRTexture* KRTextureManager::loadTexture(const char* szName, const char* szExtension, Block* data)
{
KRTexture* pTexture = NULL;
std::string lowerName = szName;
std::transform(lowerName.begin(), lowerName.end(),
lowerName.begin(), ::tolower);
std::string lowerExtension = szExtension;
std::transform(lowerExtension.begin(), lowerExtension.end(),
lowerExtension.begin(), ::tolower);
if (strcmp(szExtension, "pvr") == 0) {
pTexture = new KRTexturePVR(getContext(), data, szName);
} else if (strcmp(szExtension, "tga") == 0) {
pTexture = new KRTextureTGA(getContext(), data, szName);
} else if (strcmp(szExtension, "ktx") == 0) {
pTexture = new KRTextureKTX(getContext(), data, szName);
} else if (strcmp(szExtension, "ktx2") == 0) {
pTexture = new KRTextureKTX2(getContext(), data, szName);
}
if (pTexture) {
m_textures[lowerName] = pTexture;
}
return pTexture;
}
KRTexture* KRTextureManager::getTextureCube(const char* szName)
{
std::string lowerName = szName;
std::transform(lowerName.begin(), lowerName.end(),
lowerName.begin(), ::tolower);
unordered_map<std::string, KRTexture*>::iterator itr = m_textures.find(lowerName);
if (itr == m_textures.end()) {
// Defer resolving the texture cube until its referenced textures are ready
const char* SUFFIXES[6] = {
"_positive_x",
"_negative_x",
"_positive_y",
"_negative_y",
"_positive_z",
"_negative_z"
};
bool found_all = true;
for (int i = 0; i < 6; i++) {
std::string faceName = lowerName + SUFFIXES[i];
KRTexture* faceTexture = dynamic_cast<KRTexture2D*>(getContext().getTextureManager()->getTexture(faceName));
if (faceTexture == NULL) {
found_all = false;
}
}
if (found_all) {
KRTextureCube* pTexture = new KRTextureCube(getContext(), lowerName);
m_textures[lowerName] = pTexture;
return pTexture;
} else {
return NULL;
}
} else {
return (*itr).second;
}
}
KRTexture* KRTextureManager::getTexture(const std::string& name)
{
std::string lowerName = name;
std::transform(lowerName.begin(), lowerName.end(),
lowerName.begin(), ::tolower);
unordered_map<std::string, KRTexture*>::iterator itr = m_textures.find(lowerName);
if (itr == m_textures.end()) {
if (lowerName.length() <= 8) {
return NULL;
} else if (lowerName.compare(0, 8, "animate:", 0, 8) == 0) {
// This is an animated texture, create KRTextureAnimated's on-demand
KRTextureAnimated* pTexture = new KRTextureAnimated(getContext(), lowerName);
m_textures[lowerName] = pTexture;
return pTexture;
} else {
// Not found
// fprintf(stderr, "ERROR: Texture not found: %s\n", name.c_str());
return NULL;
}
} else {
return (*itr).second;
}
}
bool KRTextureManager::selectTexture(unsigned int target, int iTextureUnit, int iTextureHandle)
{
// TODO - Vulkan Refactoring
return true;
}
long KRTextureManager::getMemUsed()
{
return m_textureMemUsed;
}
long KRTextureManager::getMemActive()
{
long mem_active = 0;
for (std::set<KRTexture*>::iterator itr = m_activeTextures.begin(); itr != m_activeTextures.end(); itr++) {
KRTexture* activeTexture = *itr;
mem_active += activeTexture->getMemSize();
}
return mem_active;
}
void KRTextureManager::startFrame(float deltaTime)
{
// TODO - Implement proper double-buffering to reduce copy operations
m_streamerFenceMutex.lock();
if (m_streamerComplete) {
assert(m_activeTextures_streamer_copy.size() == 0); // The streamer should have emptied this if it really did complete
const long KRENGINE_TEXTURE_EXPIRY_FRAMES = 10;
std::set<KRTexture*> expiredTextures;
for (std::set<KRTexture*>::iterator itr = m_activeTextures.begin(); itr != m_activeTextures.end(); itr++) {
KRTexture* activeTexture = *itr;
activeTexture->_swapHandles();
if (activeTexture->getLastFrameUsed() + KRENGINE_TEXTURE_EXPIRY_FRAMES < getContext().getCurrentFrame()) {
// Expire textures that haven't been used in a long time
expiredTextures.insert(activeTexture);
activeTexture->releaseHandles();
} else {
float priority = activeTexture->getStreamPriority();
m_activeTextures_streamer_copy.push_back(std::pair<float, KRTexture*>(priority, activeTexture));
}
}
for (std::set<KRTexture*>::iterator itr = expiredTextures.begin(); itr != expiredTextures.end(); itr++) {
m_activeTextures.erase(*itr);
}
if (m_activeTextures_streamer_copy.size() > 0) {
m_streamerComplete = false;
}
}
m_streamerFenceMutex.unlock();
m_memoryTransferredThisFrame = 0;
}
void KRTextureManager::endFrame(float deltaTime)
{
}
void KRTextureManager::doStreaming(long& memoryRemaining, long& memoryRemainingThisFrame)
{
// TODO - Implement proper double-buffering to reduce copy operations
m_streamerFenceMutex.lock();
m_activeTextures_streamer = std::move(m_activeTextures_streamer_copy);
m_streamerFenceMutex.unlock();
if (m_activeTextures_streamer.size() > 0) {
balanceTextureMemory(memoryRemaining, memoryRemainingThisFrame);
m_streamerFenceMutex.lock();
m_streamerComplete = true;
m_streamerFenceMutex.unlock();
} else {
memoryRemaining -= getMemUsed();
}
}
void KRTextureManager::balanceTextureMemory(long& memoryRemaining, long& memoryRemainingThisFrame)
{
// Balance texture memory by reducing and increasing the maximum mip-map level of both active and inactive textures
// Favour performance over maximum texture resolution when memory is insufficient for textures at full resolution.
/*
NEW ALGORITHM:
Textures are assigned a “weight” by tuneable criteria:
- Area of screen coverage taken by objects containing material (more accurate and generic than distance)
- Type of texture (separate weight for normal, diffuse, spec maps)
- Last used time (to keep textures loaded for recently seen objects that are outside of the view frustum)
Those factors combine together to give a “weight”, which represents a proportion relative to all other textures weights
Mipmap levels are stripped off of each texture until they occupy the amount of memory they should proportionally have
This is in contrast to the global ceiling of texture resolution that slowly drops until the textures fit
*/
// ---------------
//long MAX_STREAM_TIME = 66;
//long startTime = getContext().getAbsoluteTimeMilliseconds();
std::sort(m_activeTextures_streamer.begin(), m_activeTextures_streamer.end(), std::greater<std::pair<float, KRTexture*>>());
for (auto itr = m_activeTextures_streamer.begin(); itr != m_activeTextures_streamer.end(); itr++) {
KRTexture* texture = (*itr).second;
int min_mip_level = KRMAX(getContext().KRENGINE_MIN_TEXTURE_DIM, texture->getMinMipMap());
long minLodMem = texture->getMemRequiredForSize(min_mip_level);
memoryRemaining -= minLodMem;
if (memoryRemainingThisFrame > minLodMem && texture->getNewLodMaxDim() < min_mip_level) {
memoryRemainingThisFrame -= minLodMem;
texture->resize(min_mip_level);
}
}
//long minMipTime = getContext().getAbsoluteTimeMilliseconds() - startTime;
std::vector<int> mipPercents = { 75, 75, 50, 50, 50 };
int mip_drop = -1;
auto mip_itr = mipPercents.begin();
long memoryRemainingThisMip = 0;
for (auto itr = m_activeTextures_streamer.begin(); itr != m_activeTextures_streamer.end(); itr++) {
if (memoryRemainingThisMip <= 0) {
if (mip_itr == mipPercents.end()) {
break;
} else {
memoryRemainingThisMip = memoryRemaining / 100L * (long)(*mip_itr);
mip_drop++;
mip_itr++;
}
}
KRTexture* texture = (*itr).second;
int min_mip_level = KRMAX(getContext().KRENGINE_MIN_TEXTURE_DIM, texture->getMinMipMap());
int max_mip_level = KRMIN(getContext().KRENGINE_MAX_TEXTURE_DIM, texture->getMaxMipMap());
int target_mip_level = (max_mip_level >> mip_drop);
long targetMem = texture->getMemRequiredForSize(target_mip_level);
long additionalMemRequired = targetMem - texture->getMemRequiredForSize(min_mip_level);
memoryRemainingThisMip -= additionalMemRequired;
memoryRemaining -= additionalMemRequired;
if (memoryRemainingThisMip > 0 && memoryRemainingThisFrame > targetMem) {
int current_mip_level = texture->getNewLodMaxDim();
if (current_mip_level == (target_mip_level >> 1) || target_mip_level < current_mip_level) {
memoryRemainingThisFrame -= targetMem;
texture->resize(target_mip_level);
} else if (current_mip_level == (target_mip_level >> 2)) {
memoryRemainingThisFrame -= texture->getMemRequiredForSize(target_mip_level >> 1);
texture->resize(target_mip_level >> 1);
} else if (current_mip_level < (target_mip_level >> 2)) {
memoryRemainingThisFrame -= texture->getMemRequiredForSize(target_mip_level >> 2);
texture->resize(target_mip_level >> 2);
}
}
//if(getContext().getAbsoluteTimeMilliseconds() - startTime > MAX_STREAM_TIME) {
// return; // Bail out early if we spend too long
//}
}
//long streamerTime = getContext().getAbsoluteTimeMilliseconds() - startTime;
//fprintf(stderr, "%i / %i\n", (int)minMipTime, (int)streamerTime);
}
long KRTextureManager::getMemoryTransferedThisFrame()
{
return m_memoryTransferredThisFrame;
}
void KRTextureManager::addMemoryTransferredThisFrame(long memoryTransferred)
{
m_memoryTransferredThisFrame += memoryTransferred;
}
void KRTextureManager::memoryChanged(long memoryDelta)
{
m_textureMemUsed += memoryDelta;
//fprintf(stderr, "Texture Memory: %ld / %i\n", (long)m_textureMemUsed, KRContext::KRENGINE_GPU_MEM_MAX);
}
unordered_map<std::string, KRTexture*>& KRTextureManager::getTextures()
{
return m_textures;
}
void KRTextureManager::compress(bool premultiply_alpha)
{
std::vector<KRTexture*> textures_to_remove;
std::vector<KRTexture*> textures_to_add;
for (unordered_map<std::string, KRTexture*>::iterator itr = m_textures.begin(); itr != m_textures.end(); itr++) {
KRTexture* texture = (*itr).second;
KRTexture* compressed_texture = texture->compress(premultiply_alpha);
if (compressed_texture) {
textures_to_remove.push_back(texture);
textures_to_add.push_back(compressed_texture);
} else {
assert(false);
}
}
for (std::vector<KRTexture*>::iterator itr = textures_to_remove.begin(); itr != textures_to_remove.end(); itr++) {
KRTexture* texture = *itr;
std::string lowerName = texture->getName();
std::transform(lowerName.begin(), lowerName.end(),
lowerName.begin(), ::tolower);
m_textures.erase(lowerName);
delete texture;
}
for (std::vector<KRTexture*>::iterator itr = textures_to_add.begin(); itr != textures_to_add.end(); itr++) {
KRTexture* texture = *itr;
std::string lowerName = texture->getName();
std::transform(lowerName.begin(), lowerName.end(),
lowerName.begin(), ::tolower);
m_textures[lowerName] = texture;
}
}
std::set<KRTexture*>& KRTextureManager::getActiveTextures()
{
return m_activeTextures;
}
void KRTextureManager::primeTexture(KRTexture* texture)
{
if (m_activeTextures.find(texture) == m_activeTextures.end()) {
m_activeTextures.insert(texture);
}
}

View File

@@ -0,0 +1,102 @@
//
// KRTextureManager.h
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#pragma once
#include "KREngine-common.h"
#include "resources/KRResourceManager.h"
#include "KRTexture.h"
#include "KRContextObject.h"
#include "KREngine-common.h"
#include "block.h"
#include "KRContext.h"
#include "KRStreamerThread.h"
class KRTextureManager : public KRResourceManager
{
public:
KRTextureManager(KRContext& context);
virtual ~KRTextureManager();
void destroy();
virtual KRResource* loadResource(const std::string& name, const std::string& extension, mimir::Block* data) override;
virtual KRResource* getResource(const std::string& name, const std::string& extension) override;
bool selectTexture(unsigned int target, int iTextureUnit, int iTextureHandle);
KRTexture* loadTexture(const char* szName, const char* szExtension, mimir::Block* data);
KRTexture* getTextureCube(const char* szName);
KRTexture* getTexture(const std::string& name);
long getMemUsed();
long getMemActive();
long getMemoryTransferedThisFrame();
void addMemoryTransferredThisFrame(long memoryTransferred);
void memoryChanged(long memoryDelta);
void startFrame(float deltaTime);
void endFrame(float deltaTime);
unordered_map<std::string, KRTexture*>& getTextures();
void compress(bool premultiply_alpha = false);
std::set<KRTexture*>& getActiveTextures();
void setMaxAnisotropy(float max_anisotropy);
void doStreaming(long& memoryRemaining, long& memoryRemainingThisFrame);
void primeTexture(KRTexture* texture);
private:
long m_memoryTransferredThisFrame;
unordered_map<std::string, KRTexture*> m_textures;
float m_maxAnisotropy;
std::set<KRTexture*> m_activeTextures;
std::vector<std::pair<float, KRTexture*> > m_activeTextures_streamer;
std::vector<std::pair<float, KRTexture*> > m_activeTextures_streamer_copy;
bool m_streamerComplete;
std::atomic<long> m_textureMemUsed;
void balanceTextureMemory(long& memoryRemaining, long& memoryRemainingThisFrame);
std::mutex m_streamerFenceMutex;
};

View File

@@ -0,0 +1,278 @@
//
// KRTexturePVR.cpp
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#include "KRTexturePVR.h"
#include "KRTextureManager.h"
#include "KREngine-common.h"
using namespace mimir;
using namespace hydra;
#define PVR_TEXTURE_FLAG_TYPE_MASK 0xff
static char gPVRTexIdentifier[5] = "PVR!";
enum
{
kPVRTextureFlagTypePVRTC_2 = 24,
kPVRTextureFlagTypePVRTC_4
};
typedef struct _PVRTexHeader
{
uint32_t headerLength;
uint32_t height;
uint32_t width;
uint32_t numMipmaps;
uint32_t flags;
uint32_t dataLength;
uint32_t bpp;
uint32_t bitmaskRed;
uint32_t bitmaskGreen;
uint32_t bitmaskBlue;
uint32_t bitmaskAlpha;
uint32_t pvrTag;
uint32_t numSurfs;
} PVRTexHeader;
KRTexturePVR::KRTexturePVR(KRContext& context, Block* data, std::string name) : KRTexture2D(context, data, name)
{
#if TARGET_OS_IPHONE
PVRTexHeader header;
m_pData->copy(&header, 0, sizeof(PVRTexHeader));
uint32_t formatFlags = header.flags & PVR_TEXTURE_FLAG_TYPE_MASK;
if (formatFlags == kPVRTextureFlagTypePVRTC_4) {
m_internalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
} else if (formatFlags == kPVRTextureFlagTypePVRTC_2) {
m_internalFormat = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
} else {
assert(false);
}
uint32_t pvrTag = header.pvrTag;
if (gPVRTexIdentifier[0] != ((pvrTag >> 0) & 0xff) ||
gPVRTexIdentifier[1] != ((pvrTag >> 8) & 0xff) ||
gPVRTexIdentifier[2] != ((pvrTag >> 16) & 0xff) ||
gPVRTexIdentifier[3] != ((pvrTag >> 24) & 0xff)) {
assert(false);
}
m_iWidth = header.width; // Note: call __builtin_bswap32 when needed to switch endianness
m_iHeight = header.height;
m_bHasAlpha = header.bitmaskAlpha;
uint32_t dataStart = sizeof(PVRTexHeader);
uint32_t dataLength = header.dataLength, dataOffset = 0, dataSize = 0;
uint32_t width = m_iWidth, height = m_iHeight, bpp = 4;
uint32_t blockSize = 0, widthBlocks = 0, heightBlocks = 0;
// Calculate the data size for each texture level and respect the minimum number of blocks
while (dataOffset < dataLength) {
if (formatFlags == kPVRTextureFlagTypePVRTC_4) {
blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
widthBlocks = width / 4;
heightBlocks = height / 4;
bpp = 4;
} else {
blockSize = 8 * 4; // Pixel by pixel block size for 2bpp
widthBlocks = width / 8;
heightBlocks = height / 4;
bpp = 2;
}
// Clamp to minimum number of blocks
if (widthBlocks < 2) {
widthBlocks = 2;
}
if (heightBlocks < 2) {
heightBlocks = 2;
}
dataSize = widthBlocks * heightBlocks * ((blockSize * bpp) / 8);
m_blocks.push_back(m_pData->getSubBlock(dataStart + dataOffset, dataSize));
dataOffset += dataSize;
width = width >> 1;
if (width < 1) {
width = 1;
}
height = height >> 1;
if (height < 1) {
height = 1;
}
}
m_max_lod_max_dim = m_iWidth > m_iHeight ? m_iWidth : m_iHeight;
m_min_lod_max_dim = width > height ? width : height;
#endif
}
KRTexturePVR::~KRTexturePVR()
{
for (std::list<Block*>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) {
Block* block = *itr;
delete block;
}
m_blocks.clear();
}
Vector2i KRTexturePVR::getDimensions() const
{
return Vector2i::Create(m_iWidth, m_iHeight);
}
VkFormat KRTexturePVR::getFormat() const
{
PVRTexHeader header;
m_pData->copy(&header, 0, sizeof(PVRTexHeader));
uint32_t formatFlags = header.flags & PVR_TEXTURE_FLAG_TYPE_MASK;
switch (formatFlags) {
case kPVRTextureFlagTypePVRTC_2:
return VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG;
case kPVRTextureFlagTypePVRTC_4:
return VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG;
break;
default:
return VK_FORMAT_UNDEFINED;
break;
}
}
long KRTexturePVR::getMemRequiredForSize(int max_dim)
{
int target_dim = max_dim;
if (target_dim < (int)m_min_lod_max_dim) target_dim = target_dim;
// Determine how much memory will be consumed
int width = m_iWidth;
int height = m_iHeight;
long memoryRequired = 0;
for (std::list<Block*>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) {
Block* block = *itr;
if (width <= target_dim && height <= target_dim) {
memoryRequired += (long)block->getSize();
}
width = width >> 1;
if (width < 1) {
width = 1;
}
height = height >> 1;
if (height < 1) {
height = 1;
}
}
return memoryRequired;
}
bool KRTexturePVR::uploadTexture(KRDevice& device, VkImage& image, int lod_max_dim, int& current_lod_max_dim, bool premultiply_alpha)
{
int target_dim = lod_max_dim;
if (target_dim < (int)m_min_lod_max_dim) target_dim = m_min_lod_max_dim;
if (m_blocks.size() == 0) {
return false;
}
// Determine how much memory will be consumed
int width = m_iWidth;
int height = m_iHeight;
long memoryRequired = 0;
long memoryTransferred = 0;
// Upload texture data
int destination_level = 0;
int source_level = 0;
for (std::list<Block*>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) {
Block* block = *itr;
if (width <= target_dim && height <= target_dim) {
if (width > current_lod_max_dim) {
current_lod_max_dim = width;
}
if (height > current_lod_max_dim) {
current_lod_max_dim = height;
}
block->lock();
/*
* TODO - Vulkan Refactoring
GLDEBUG(glCompressedTexImage2D(target, destination_level, m_internalFormat, width, height, 0, (int)block->getSize(), block->getStart()));
*/
block->unlock();
memoryTransferred += (long)block->getSize(); // memoryTransferred does not include throughput of mipmap levels copied through glCopyTextureLevelsAPPLE
memoryRequired += (long)block->getSize();
//
// err = glGetError();
// if (err != GL_NO_ERROR) {
// assert(false);
// return false;
// }
//
destination_level++;
}
if (width <= m_current_lod_max_dim && height <= m_current_lod_max_dim) {
source_level++;
}
width = width >> 1;
if (width < 1) {
width = 1;
}
height = height >> 1;
if (height < 1) {
height = 1;
}
}
return true;
}
std::string KRTexturePVR::getExtension()
{
return "pvr";
}
int KRTexturePVR::getFaceCount() const
{
return 1;
}

View File

@@ -0,0 +1,58 @@
//
// KRTexturePVR.h
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#pragma once
#include "KRTexture2D.h"
class KRTexturePVR : public KRTexture2D
{
public:
KRTexturePVR(KRContext& context, mimir::Block* data, std::string name);
virtual ~KRTexturePVR();
virtual std::string getExtension() override;
bool uploadTexture(KRDevice& device, VkImage& image, int lod_max_dim, int& current_lod_max_dim, bool premultiply_alpha = false) override;
virtual long getMemRequiredForSize(int max_dim) override;
virtual hydra::Vector2i getDimensions() const override;
virtual VkFormat getFormat() const override;
virtual int getFaceCount() const override;
protected:
uint32_t m_iWidth;
uint32_t m_iHeight;
unsigned int m_internalFormat;
bool m_bHasAlpha;
std::list<mimir::Block*> m_blocks;
};

View File

@@ -0,0 +1,428 @@
//
// KRTextureTGA.cpp
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#include "KRTextureTGA.h"
#include "KREngine-common.h"
#include "KRContext.h"
#include "KRTextureKTX2.h"
using namespace hydra;
#if defined(_WIN32) || defined(_WIN64)
#pragma pack(1)
typedef struct
{
char idlength;
char colourmaptype;
char imagetype;
short int colourmaporigin;
short int colourmaplength;
char colourmapdepth;
short int x_origin;
short int y_origin;
short width;
short height;
char bitsperpixel;
char imagedescriptor;
} TGA_HEADER;
#pragma pack()
#else
typedef struct
{
char idlength;
char colourmaptype;
char imagetype;
short int colourmaporigin;
short int colourmaplength;
char colourmapdepth;
short int x_origin;
short int y_origin;
short width;
short height;
char bitsperpixel;
char imagedescriptor;
} __attribute__((packed)) TGA_HEADER;
#endif
KRTextureTGA::KRTextureTGA(KRContext& context, Block* data, std::string name) : KRTexture2D(context, data, name)
{
data->lock();
TGA_HEADER* pHeader = (TGA_HEADER*)data->getStart();
m_dimensions.x = pHeader->width;
m_dimensions.y = pHeader->height;
m_max_lod_max_dim = pHeader->width > pHeader->height ? pHeader->width : pHeader->height;
m_min_lod_max_dim = m_max_lod_max_dim; // Mipmaps not yet supported for TGA images
switch (pHeader->imagetype) {
case 2: // rgb
case 10: // rgb + rle
switch (pHeader->bitsperpixel) {
case 24:
{
m_imageSize = pHeader->width * pHeader->height * 4;
}
break;
case 32:
{
m_imageSize = pHeader->width * pHeader->height * 4;
}
break;
default:
{
assert(false);
}
break;
}
break;
default:
{
assert(false);
break;
}
}
data->unlock();
}
KRTextureTGA::~KRTextureTGA()
{
}
bool KRTextureTGA::uploadTexture(KRDevice& device, VkImage& image, int lod_max_dim, int& current_lod_max_dim, bool premultiply_alpha)
{
// TODO - Vulkan Refactoring - Perhaps it would be more efficient to reformat the color channels during the copy to the staging buffer.
m_pData->lock();
TGA_HEADER* pHeader = (TGA_HEADER*)m_pData->getStart();
unsigned char* pData = (unsigned char*)pHeader + (long)pHeader->idlength + (long)pHeader->colourmaplength * (long)pHeader->colourmaptype + sizeof(TGA_HEADER);
if (pHeader->colourmaptype != 0) {
m_pData->unlock();
return false; // Mapped colors not supported
}
Vector3i dimensions = { pHeader->width, pHeader->height, 1 };
switch (pHeader->imagetype) {
case 2: // rgb
switch (pHeader->bitsperpixel) {
case 24:
{
unsigned char* converted_image = (unsigned char*)malloc(pHeader->width * pHeader->height * 4);
unsigned char* pSource = pData;
unsigned char* pDest = converted_image;
unsigned char* pEnd = pData + pHeader->height * pHeader->width * 3;
while (pSource < pEnd) {
*pDest++ = pSource[2];
*pDest++ = pSource[1];
*pDest++ = pSource[0];
*pDest++ = 0xff;
pSource += 3;
}
assert(pSource <= m_pData->getEnd());
device.streamUpload((void*)converted_image, pDest - converted_image, dimensions, image);
free(converted_image);
current_lod_max_dim = m_max_lod_max_dim;
}
break;
case 32:
{
if (premultiply_alpha) {
unsigned char* converted_image = (unsigned char*)malloc(pHeader->width * pHeader->height * 4);
unsigned char* pSource = pData;
unsigned char* pDest = converted_image;
unsigned char* pEnd = pData + pHeader->height * pHeader->width * 3;
while (pSource < pEnd) {
*pDest++ = (__uint32_t)pSource[2] * (__uint32_t)pSource[3] / 0xff;
*pDest++ = (__uint32_t)pSource[1] * (__uint32_t)pSource[3] / 0xff;
*pDest++ = (__uint32_t)pSource[0] * (__uint32_t)pSource[3] / 0xff;
*pDest++ = pSource[3];
pSource += 4;
}
assert(pSource <= m_pData->getEnd());
device.streamUpload((void*)converted_image, pDest - converted_image, dimensions, image);
free(converted_image);
} else {
unsigned char* converted_image = (unsigned char*)malloc(pHeader->width * pHeader->height * 4);
unsigned char* pSource = pData;
unsigned char* pDest = converted_image;
unsigned char* pEnd = pData + pHeader->height * pHeader->width * 3;
while (pSource < pEnd) {
*pDest++ = (__uint32_t)pSource[2];
*pDest++ = (__uint32_t)pSource[1];
*pDest++ = (__uint32_t)pSource[0];
*pDest++ = pSource[3];
pSource += 4;
}
assert(pSource <= m_pData->getEnd());
device.streamUpload((void*)converted_image, pDest - converted_image, dimensions, image);
free(converted_image);
}
current_lod_max_dim = m_max_lod_max_dim;
}
break;
default:
m_pData->unlock();
return false; // 16-bit images not yet supported
}
break;
case 10: // rgb + rle
switch (pHeader->bitsperpixel) {
case 32:
{
unsigned char* converted_image = (unsigned char*)malloc(pHeader->width * pHeader->height * 4);
unsigned char* pSource = pData;
unsigned char* pDest = converted_image;
unsigned char* pEnd = converted_image + pHeader->height * pHeader->width * 4;
if (premultiply_alpha) {
while (pDest < pEnd) {
int count = (*pSource & 0x7f) + 1;
if (*pSource & 0x80) {
// RLE Packet
pSource++;
while (count--) {
*pDest++ = (__uint32_t)pSource[2] * (__uint32_t)pSource[3] / 0xff;
*pDest++ = (__uint32_t)pSource[1] * (__uint32_t)pSource[3] / 0xff;
*pDest++ = (__uint32_t)pSource[0] * (__uint32_t)pSource[3] / 0xff;
*pDest++ = pSource[3];
}
pSource += 4;
} else {
// RAW Packet
pSource++;
while (count--) {
*pDest++ = (__uint32_t)pSource[2] * (__uint32_t)pSource[3] / 0xff;
*pDest++ = (__uint32_t)pSource[1] * (__uint32_t)pSource[3] / 0xff;
*pDest++ = (__uint32_t)pSource[0] * (__uint32_t)pSource[3] / 0xff;
*pDest++ = pSource[3];
pSource += 4;
}
}
}
assert(pSource <= m_pData->getEnd());
assert(pDest == pEnd);
} else {
while (pDest < pEnd) {
int count = (*pSource & 0x7f) + 1;
if (*pSource & 0x80) {
// RLE Packet
pSource++;
while (count--) {
*pDest++ = pSource[2];
*pDest++ = pSource[1];
*pDest++ = pSource[0];
*pDest++ = pSource[3];
}
pSource += 4;
} else {
// RAW Packet
pSource++;
while (count--) {
*pDest++ = pSource[2];
*pDest++ = pSource[1];
*pDest++ = pSource[0];
*pDest++ = pSource[3];
pSource += 4;
}
}
}
assert(pSource <= m_pData->getEnd());
assert(pDest == pEnd);
}
device.streamUpload((void*)converted_image, pDest - converted_image, dimensions, image);
free(converted_image);
current_lod_max_dim = m_max_lod_max_dim;
}
break;
case 24:
{
unsigned char* converted_image = (unsigned char*)malloc(pHeader->width * pHeader->height * 4);
unsigned char* pSource = pData;
unsigned char* pDest = converted_image;
unsigned char* pEnd = converted_image + pHeader->height * pHeader->width * 4;
while (pDest < pEnd) {
int count = (*pSource & 0x7f) + 1;
if (*pSource & 0x80) {
// RLE Packet
pSource++;
while (count--) {
*pDest++ = pSource[2];
*pDest++ = pSource[1];
*pDest++ = pSource[0];
*pDest++ = 0xff;
}
pSource += 3;
} else {
// RAW Packet
pSource++;
while (count--) {
*pDest++ = pSource[2];
*pDest++ = pSource[1];
*pDest++ = pSource[0];
*pDest++ = 0xff;
pSource += 3;
}
}
}
assert(pSource <= m_pData->getEnd());
assert(pDest == pEnd);
device.streamUpload((void*)converted_image, pDest - converted_image, dimensions, image);
free(converted_image);
current_lod_max_dim = m_max_lod_max_dim;
}
break;
default:
m_pData->unlock();
return false; // 16-bit images not yet supported
}
break;
default:
m_pData->unlock();
return false; // Image type not yet supported
}
m_pData->unlock();
return true;
}
#if !TARGET_OS_IPHONE && !defined(ANDROID)
KRTexture* KRTextureTGA::compress(bool premultiply_alpha)
{
// TODO - Vulkan refactoring...
assert(false);
return nullptr;
/*
* TODO - Vulkan refactoring...
m_pData->lock();
std::list<Block *> blocks;
getContext().getTextureManager()->_setActiveTexture(0);
int compressed_handle = 0;
GLDEBUG(glGenTextures(1, &compressed_handle));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, compressed_handle));
int current_max_dim = 0;
if(!uploadTexture(m_max_lod_max_dim, current_max_dim, premultiply_alpha)) {
assert(false); // Failed to upload the texture
}
GLDEBUG(glGenerateMipmap(GL_TEXTURE_2D));
GLint width = 0, height = 0, internal_format, base_internal_format;
GLDEBUG(glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width));
GLDEBUG(glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height));
GLDEBUG(glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &internal_format));
switch(internal_format)
{
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
base_internal_format = GL_BGRA;
break;
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
base_internal_format = GL_BGRA;
break;
default:
assert(false); // Not yet supported
break;
}
int lod_level = 0;
GLint compressed_size = 0;
int lod_width = width;
while(lod_width > 1) {
GLDEBUG(glGetTexLevelParameteriv(GL_TEXTURE_2D, lod_level, GL_TEXTURE_WIDTH, &lod_width));
GLDEBUG(glGetTexLevelParameteriv(GL_TEXTURE_2D, lod_level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &compressed_size));
Block *new_block = new Block();
new_block->expand(compressed_size);
new_block->lock();
GLDEBUG(glGetCompressedTexImage(GL_TEXTURE_2D, lod_level, new_block->getStart()));
new_block->unlock();
blocks.push_back(new_block);
lod_level++;
}
assert(lod_width == 1);
GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0));
getContext().getTextureManager()->selectTexture(0, NULL, 0.0f, KRTexture::TEXTURE_USAGE_NONE);
GLDEBUG(glDeleteTextures(1, &compressed_handle));
KRTextureKTX *new_texture = new KRTextureKTX(getContext(), getName(), internal_format, base_internal_format, width, height, blocks);
m_pData->unlock();
for(auto block_itr = blocks.begin(); block_itr != blocks.end(); block_itr++) {
Block *block = *block_itr;
delete block;
}
return new_texture;
*/
}
#endif
long KRTextureTGA::getMemRequiredForSize(int max_dim)
{
return m_imageSize;
}
Vector2i KRTextureTGA::getDimensions() const
{
return m_dimensions;
}
VkFormat KRTextureTGA::getFormat() const
{
// TODO - We should not automatically add the alpha channel on import
return VK_FORMAT_R8G8B8A8_SRGB;
}
std::string KRTextureTGA::getExtension()
{
return "tga";
}
int KRTextureTGA::getFaceCount() const
{
return 1;
}

View File

@@ -0,0 +1,58 @@
//
// KRTextureTGA.h
// Kraken Engine
//
// Copyright 2024 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#pragma once
#include "KRTexture2D.h"
using namespace mimir;
class KRTextureTGA : public KRTexture2D
{
public:
KRTextureTGA(KRContext& context, Block* data, std::string name);
virtual ~KRTextureTGA();
virtual std::string getExtension() override;
bool uploadTexture(KRDevice& device, VkImage& image, int lod_max_dim, int& current_lod_max_dim, bool premultiply_alpha = false) override;
#if !TARGET_OS_IPHONE && !defined(ANDROID)
virtual KRTexture* compress(bool premultiply_alpha = false) override;
#endif
virtual long getMemRequiredForSize(int max_dim) override;
virtual hydra::Vector2i getDimensions() const override;
virtual VkFormat getFormat() const override;
virtual int getFaceCount() const override;
private:
long m_imageSize;
hydra::Vector2i m_dimensions;
};