material.cpp
1 // Copyright 2023 Citra Emulator Project 2 // Licensed under GPLv2 or any later version 3 // Refer to the license.txt file included. 4 5 #include "common/file_util.h" 6 #include "common/logging/log.h" 7 #include "common/texture.h" 8 #include "core/frontend/image_interface.h" 9 #include "video_core/custom_textures/material.h" 10 11 namespace VideoCore { 12 13 namespace { 14 15 CustomPixelFormat ToCustomPixelFormat(ddsktx_format format) { 16 switch (format) { 17 case DDSKTX_FORMAT_RGBA8: 18 return CustomPixelFormat::RGBA8; 19 case DDSKTX_FORMAT_BC1: 20 return CustomPixelFormat::BC1; 21 case DDSKTX_FORMAT_BC3: 22 return CustomPixelFormat::BC3; 23 case DDSKTX_FORMAT_BC5: 24 return CustomPixelFormat::BC5; 25 case DDSKTX_FORMAT_BC7: 26 return CustomPixelFormat::BC7; 27 case DDSKTX_FORMAT_ASTC4x4: 28 return CustomPixelFormat::ASTC4; 29 case DDSKTX_FORMAT_ASTC6x6: 30 return CustomPixelFormat::ASTC6; 31 case DDSKTX_FORMAT_ASTC8x6: 32 return CustomPixelFormat::ASTC8; 33 default: 34 LOG_ERROR(Common, "Unknown dds/ktx pixel format {}", format); 35 return CustomPixelFormat::RGBA8; 36 } 37 } 38 39 std::string_view MapTypeName(MapType type) { 40 switch (type) { 41 case MapType::Color: 42 return "Color"; 43 case MapType::Normal: 44 return "Normal"; 45 default: 46 return "Invalid"; 47 } 48 } 49 50 } // Anonymous namespace 51 52 CustomTexture::CustomTexture(Frontend::ImageInterface& image_interface_) 53 : image_interface{image_interface_} {} 54 55 CustomTexture::~CustomTexture() = default; 56 57 void CustomTexture::LoadFromDisk(bool flip_png) { 58 std::scoped_lock lock{decode_mutex}; 59 if (IsLoaded()) { 60 return; 61 } 62 63 FileUtil::IOFile file{path, "rb"}; 64 std::vector<u8> input(file.GetSize()); 65 if (file.ReadBytes(input.data(), input.size()) != input.size()) { 66 LOG_CRITICAL(Render, "Failed to open custom texture: {}", path); 67 return; 68 } 69 switch (file_format) { 70 case CustomFileFormat::PNG: 71 LoadPNG(input, flip_png); 72 break; 73 case CustomFileFormat::DDS: 74 case CustomFileFormat::KTX: 75 LoadDDS(input); 76 break; 77 default: 78 LOG_ERROR(Render, "Unknown file format {}", file_format); 79 } 80 } 81 82 void CustomTexture::LoadPNG(std::span<const u8> input, bool flip_png) { 83 if (!image_interface.DecodePNG(data, width, height, input)) { 84 LOG_ERROR(Render, "Failed to decode png: {}", path); 85 return; 86 } 87 if (flip_png) { 88 Common::FlipRGBA8Texture(data, width, height); 89 } 90 format = CustomPixelFormat::RGBA8; 91 } 92 93 void CustomTexture::LoadDDS(std::span<const u8> input) { 94 ddsktx_format dds_format{}; 95 image_interface.DecodeDDS(data, width, height, dds_format, input); 96 format = ToCustomPixelFormat(dds_format); 97 } 98 99 void Material::LoadFromDisk(bool flip_png) noexcept { 100 if (IsDecoded()) { 101 return; 102 } 103 for (CustomTexture* const texture : textures) { 104 if (!texture || texture->IsLoaded()) { 105 continue; 106 } 107 texture->LoadFromDisk(flip_png); 108 size += texture->data.size(); 109 LOG_DEBUG(Render, "Loading {} map {}", MapTypeName(texture->type), texture->path); 110 } 111 if (!textures[0]) { 112 LOG_ERROR(Render, "Unable to create material without color texture!"); 113 state = DecodeState::Failed; 114 return; 115 } 116 width = textures[0]->width; 117 height = textures[0]->height; 118 format = textures[0]->format; 119 for (const CustomTexture* texture : textures) { 120 if (!texture) { 121 continue; 122 } 123 if (texture->width != width || texture->height != height) { 124 LOG_ERROR(Render, 125 "{} map {} of material with hash {:#016X} has dimentions {}x{} " 126 "which do not match the color texture dimentions {}x{}", 127 MapTypeName(texture->type), texture->path, hash, texture->width, 128 texture->height, width, height); 129 state = DecodeState::Failed; 130 return; 131 } 132 if (texture->format != format) { 133 LOG_ERROR( 134 Render, "{} map {} is stored with {} format which does not match color format {}", 135 MapTypeName(texture->type), texture->path, 136 CustomPixelFormatAsString(texture->format), CustomPixelFormatAsString(format)); 137 state = DecodeState::Failed; 138 return; 139 } 140 } 141 state = DecodeState::Decoded; 142 } 143 144 void Material::AddMapTexture(CustomTexture* texture) noexcept { 145 const std::size_t index = static_cast<std::size_t>(texture->type); 146 if (textures[index]) { 147 LOG_ERROR(Render, "Textures {} and {} are assigned to the same material, ignoring!", 148 textures[index]->path, texture->path); 149 return; 150 } 151 textures[index] = texture; 152 } 153 154 } // namespace VideoCore