56 | 56 |
namespace
|
57 | 57 |
{
|
58 | 58 |
|
59 | |
struct TextureInitData
|
60 | |
{
|
|
59 |
struct TextureInitData : public ObjectBase<IObject>
|
|
60 |
{
|
|
61 |
TextureInitData(IReferenceCounters* pRefCounters) :
|
|
62 |
ObjectBase<IObject>{pRefCounters}
|
|
63 |
{}
|
|
64 |
|
61 | 65 |
struct LevelData
|
62 | 66 |
{
|
63 | 67 |
std::vector<unsigned char> Data;
|
64 | 68 |
|
65 | 69 |
Uint32 Stride = 0;
|
66 | |
Box UpdateBox;
|
|
70 |
Uint32 Width = 0;
|
|
71 |
Uint32 Height = 0;
|
67 | 72 |
};
|
68 | 73 |
std::vector<LevelData> Levels;
|
69 | 74 |
|
|
74 | 79 |
|
75 | 80 |
struct Model::ResourceInitData
|
76 | 81 |
{
|
77 | |
std::vector<TextureInitData> Textures;
|
78 | |
|
79 | 82 |
std::vector<Uint32> IndexData;
|
80 | 83 |
std::vector<VertexBasicAttribs> VertexBasicData;
|
81 | 84 |
std::vector<VertexSkinAttribs> VertexSkinData;
|
82 | 85 |
};
|
83 | 86 |
|
84 | 87 |
|
85 | |
static TextureInitData PrepareGLTFTextureInitData(
|
|
88 |
static RefCntAutoPtr<TextureInitData> PrepareGLTFTextureInitData(
|
86 | 89 |
const tinygltf::Image& gltfimage,
|
87 | 90 |
float AlphaCutoff,
|
88 | |
Uint32 DstX,
|
89 | |
Uint32 DstY,
|
90 | 91 |
Uint32 NumMipLevels)
|
91 | 92 |
{
|
92 | 93 |
VERIFY_EXPR(!gltfimage.image.empty());
|
93 | 94 |
VERIFY_EXPR(gltfimage.width > 0 && gltfimage.height > 0 && gltfimage.component > 0);
|
94 | 95 |
|
95 | |
TextureInitData UpdateInfo;
|
96 | |
|
97 | |
auto& Levels = UpdateInfo.Levels;
|
|
96 |
RefCntAutoPtr<TextureInitData> UpdateInfo{MakeNewRCObj<TextureInitData>()()};
|
|
97 |
|
|
98 |
auto& Levels = UpdateInfo->Levels;
|
98 | 99 |
Levels.resize(NumMipLevels);
|
99 | 100 |
|
100 | |
auto& Level0 = Levels[0];
|
101 | |
Level0.Stride = gltfimage.width * 4;
|
102 | |
Level0.UpdateBox.MinX = DstX;
|
103 | |
Level0.UpdateBox.MaxX = DstX + static_cast<Uint32>(gltfimage.width);
|
104 | |
Level0.UpdateBox.MinY = DstY;
|
105 | |
Level0.UpdateBox.MaxY = DstY + static_cast<Uint32>(gltfimage.height);
|
|
101 |
auto& Level0 = Levels[0];
|
|
102 |
Level0.Width = static_cast<Uint32>(gltfimage.width);
|
|
103 |
Level0.Height = static_cast<Uint32>(gltfimage.height);
|
|
104 |
Level0.Stride = Level0.Width * 4;
|
106 | 105 |
|
107 | 106 |
if (gltfimage.component == 3)
|
108 | 107 |
{
|
|
177 | 176 |
const auto MipHeight = std::max(FineMipHeight / 2u, 1u);
|
178 | 177 |
|
179 | 178 |
Level.Stride = MipWidth * 4;
|
|
179 |
Level.Width = MipWidth;
|
|
180 |
Level.Height = MipHeight;
|
180 | 181 |
Level.Data.resize(Level.Stride * MipHeight);
|
181 | |
Level.UpdateBox.MinX = DstX >> mip;
|
182 | |
Level.UpdateBox.MaxX = Level.UpdateBox.MinX + MipWidth;
|
183 | |
Level.UpdateBox.MinY = DstY >> mip;
|
184 | |
Level.UpdateBox.MaxY = Level.UpdateBox.MinY + MipHeight;
|
185 | 182 |
|
186 | 183 |
ComputeMipLevel(FineMipWidth, FineMipHeight, TEX_FORMAT_RGBA8_UNORM, FineLevel.Data.data(), FineLevel.Stride,
|
187 | 184 |
Level.Data.data(), Level.Stride);
|
|
769 | 766 |
}
|
770 | 767 |
}
|
771 | 768 |
|
772 | |
TextureInitData TexInitData;
|
773 | |
|
774 | 769 |
if (!TexInfo.IsValid())
|
775 | 770 |
{
|
776 | 771 |
RefCntAutoPtr<ISampler> pSampler;
|
|
785 | 780 |
}
|
786 | 781 |
|
787 | 782 |
// Check if the texture is used in an alpha-cut material
|
788 | |
float AlphaCutoff = GetTextureAlphaCutoffValue(gltf_model, static_cast<int>(Textures.size()));
|
|
783 |
const float AlphaCutoff = GetTextureAlphaCutoffValue(gltf_model, static_cast<int>(Textures.size()));
|
789 | 784 |
|
790 | 785 |
if (gltf_image.width > 0 && gltf_image.height > 0)
|
791 | 786 |
{
|
792 | 787 |
if (pResourceMgr != nullptr)
|
793 | 788 |
{
|
|
789 |
// No reference
|
|
790 |
const TextureDesc AtlasDesc = pResourceMgr->GetAtlasDesc(TEX_FORMAT_RGBA8_UNORM);
|
|
791 |
|
|
792 |
auto pInitData = PrepareGLTFTextureInitData(gltf_image, AlphaCutoff, AtlasDesc.MipLevels);
|
|
793 |
|
|
794 |
// Init data will be atomically set in the allocation before any other thread may be able to
|
|
795 |
// get access to it.
|
|
796 |
// Note that it is possible that more than one thread prepares pInitData for the same allocation.
|
|
797 |
// It it also possible that multiple instances of the same allocation are created before the first
|
|
798 |
// is added to the cache. This is all OK though.
|
794 | 799 |
TexInfo.pAtlasSuballocation =
|
795 | |
pResourceMgr->AllocateTextureSpace(TEX_FORMAT_RGBA8_UNORM, gltf_image.width, gltf_image.height, CacheId.c_str());
|
|
800 |
pResourceMgr->AllocateTextureSpace(TEX_FORMAT_RGBA8_UNORM, gltf_image.width, gltf_image.height, CacheId.c_str(), pInitData);
|
|
801 |
|
|
802 |
VERIFY_EXPR(TexInfo.pAtlasSuballocation->GetAtlas()->GetAtlasDesc().MipLevels == AtlasDesc.MipLevels);
|
796 | 803 |
}
|
797 | 804 |
else
|
798 | 805 |
{
|
|
810 | 817 |
pDevice->CreateTexture(TexDesc, nullptr, &TexInfo.pTexture);
|
811 | 818 |
TexInfo.pTexture->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE)->SetSampler(pSampler);
|
812 | 819 |
|
813 | |
TexInitData = PrepareGLTFTextureInitData(gltf_image, AlphaCutoff, 0, 0, 1);
|
|
820 |
auto pTexInitData = PrepareGLTFTextureInitData(gltf_image, AlphaCutoff, 1);
|
|
821 |
TexInfo.pTexture->SetUserData(pTexInitData);
|
814 | 822 |
}
|
815 | 823 |
}
|
816 | 824 |
else if (gltf_image.pixel_type == IMAGE_FILE_FORMAT_DDS || gltf_image.pixel_type == IMAGE_FILE_FORMAT_KTX)
|
817 | 825 |
{
|
818 | 826 |
// Create the texture from raw bits
|
819 | |
RefCntAutoPtr<DataBlobImpl> pRawData(MakeNewRCObj<DataBlobImpl>()(gltf_image.image.size()));
|
820 | |
memcpy(pRawData->GetDataPtr(), gltf_image.image.data(), gltf_image.image.size());
|
|
827 |
|
|
828 |
RefCntAutoPtr<ITexture> pStagingTex;
|
|
829 |
|
|
830 |
RefCntAutoPtr<TextureInitData> pTexInitData{MakeNewRCObj<TextureInitData>()()};
|
|
831 |
|
821 | 832 |
TextureLoadInfo LoadInfo;
|
822 | 833 |
if (pResourceMgr != nullptr)
|
823 | 834 |
{
|
|
829 | 840 |
switch (gltf_image.pixel_type)
|
830 | 841 |
{
|
831 | 842 |
case IMAGE_FILE_FORMAT_DDS:
|
832 | |
CreateTextureFromDDS(pRawData, LoadInfo, pDevice, pResourceMgr != nullptr ? &TexInitData.pStagingTex : &TexInfo.pTexture);
|
|
843 |
CreateTextureFromDDS(gltf_image.image.data(), gltf_image.image.size(), LoadInfo, pDevice, pResourceMgr != nullptr ? &pStagingTex : &TexInfo.pTexture);
|
833 | 844 |
break;
|
834 | 845 |
|
835 | 846 |
case IMAGE_FILE_FORMAT_KTX:
|
836 | |
CreateTextureFromKTX(pRawData, LoadInfo, pDevice, &TexInfo.pTexture);
|
|
847 |
CreateTextureFromKTX(gltf_image.image.data(), gltf_image.image.size(), LoadInfo, pDevice, pResourceMgr != nullptr ? &pStagingTex : &TexInfo.pTexture);
|
837 | 848 |
break;
|
838 | 849 |
|
839 | 850 |
default:
|
840 | 851 |
UNEXPECTED("Unknown raw image format");
|
841 | 852 |
}
|
842 | |
if (pResourceMgr != nullptr && TexInitData.pStagingTex)
|
843 | |
{
|
844 | |
const auto& TexDesc = TexInitData.pStagingTex->GetDesc();
|
845 | |
TexInfo.pAtlasSuballocation = pResourceMgr->AllocateTextureSpace(TexDesc.Format, TexDesc.Width, TexDesc.Height, CacheId.c_str());
|
846 | |
}
|
847 | |
}
|
848 | |
|
849 | |
if (TexInfo.pAtlasSuballocation)
|
850 | |
{
|
851 | |
const auto& AtlasDesc = TexInfo.pAtlasSuballocation->GetAtlas()->GetAtlasDesc();
|
852 | |
const auto& Origin = TexInfo.pAtlasSuballocation->GetOrigin();
|
853 | |
|
854 | |
if (!TexInitData.pStagingTex)
|
855 | |
{
|
856 | |
TexInitData = PrepareGLTFTextureInitData(gltf_image, AlphaCutoff, Origin.x, Origin.y, AtlasDesc.MipLevels);
|
857 | |
}
|
858 | |
}
|
859 | |
|
860 | |
if (!InitData)
|
|
853 |
if (TexInfo.pTexture)
|
|
854 |
{
|
|
855 |
// Set empty init data to inidicate that the texture needs to be transitioned to correct state
|
|
856 |
TexInfo.pTexture->SetUserData(pTexInitData);
|
|
857 |
}
|
|
858 |
else if (pResourceMgr != nullptr && pStagingTex)
|
|
859 |
{
|
|
860 |
const auto& TexDesc = pStagingTex->GetDesc();
|
|
861 |
|
|
862 |
pTexInitData->pStagingTex = std::move(pStagingTex);
|
|
863 |
|
|
864 |
// Init data will be atomically set in the allocation before any other thread may be able to
|
|
865 |
// get access to it.
|
|
866 |
// Note that it is possible that more than one thread prepares pInitData for the same allocation.
|
|
867 |
// It it also possible that multiple instances of the same allocation are created before the first
|
|
868 |
// is added to the cache. This is all OK though.
|
|
869 |
TexInfo.pAtlasSuballocation = pResourceMgr->AllocateTextureSpace(TexDesc.Format, TexDesc.Width, TexDesc.Height, CacheId.c_str(), pTexInitData);
|
|
870 |
}
|
|
871 |
}
|
|
872 |
|
|
873 |
if (pResourceMgr == nullptr && !TexInfo.pTexture)
|
861 | 874 |
{
|
862 | 875 |
// Create stub texture
|
863 | 876 |
TextureDesc TexDesc;
|
|
885 | 898 |
}
|
886 | 899 |
|
887 | 900 |
Textures.emplace_back(std::move(TexInfo));
|
888 | |
InitData->Textures.emplace_back(std::move(TexInitData));
|
889 | 901 |
}
|
890 | 902 |
}
|
891 | 903 |
|
|
896 | 908 |
|
897 | 909 |
std::vector<StateTransitionDesc> Barriers;
|
898 | 910 |
|
899 | |
VERIFY_EXPR(InitData->Textures.size() == Textures.size());
|
900 | 911 |
for (Uint32 i = 0; i < Textures.size(); ++i)
|
901 | 912 |
{
|
902 | |
auto& TexInfo = Textures[i];
|
903 | |
ITexture* pTexture = TexInfo.pAtlasSuballocation ?
|
904 | |
TexInfo.pAtlasSuballocation->GetAtlas()->GetTexture(pDevice, pCtx) :
|
905 | |
TexInfo.pTexture;
|
|
913 |
auto& DstTexInfo = Textures[i];
|
|
914 |
|
|
915 |
ITexture* pTexture = nullptr;
|
|
916 |
TextureInitData* pInitData = nullptr;
|
|
917 |
if (DstTexInfo.pAtlasSuballocation)
|
|
918 |
{
|
|
919 |
pTexture = DstTexInfo.pAtlasSuballocation->GetAtlas()->GetTexture(pDevice, pCtx);
|
|
920 |
pInitData = ValidatedCast<TextureInitData>(DstTexInfo.pAtlasSuballocation->GetUserData());
|
|
921 |
}
|
|
922 |
else if (DstTexInfo.pTexture)
|
|
923 |
{
|
|
924 |
pTexture = DstTexInfo.pTexture;
|
|
925 |
pInitData = ValidatedCast<TextureInitData>(pTexture->GetUserData());
|
|
926 |
}
|
|
927 |
|
906 | 928 |
if (!pTexture)
|
907 | 929 |
continue;
|
908 | 930 |
|
909 | |
auto& TexData = InitData->Textures[i];
|
910 | |
|
911 | |
const auto& Levels = TexData.Levels;
|
912 | |
const auto DstSlice = TexInfo.pAtlasSuballocation ? TexInfo.pAtlasSuballocation->GetSlice() : 0;
|
|
931 |
if (pInitData == nullptr)
|
|
932 |
{
|
|
933 |
// Texture data has already been initialized by another model
|
|
934 |
continue;
|
|
935 |
}
|
|
936 |
|
|
937 |
const auto& Levels = pInitData->Levels;
|
|
938 |
const auto DstSlice = DstTexInfo.pAtlasSuballocation ? DstTexInfo.pAtlasSuballocation->GetSlice() : 0;
|
913 | 939 |
if (!Levels.empty())
|
914 | 940 |
{
|
|
941 |
Uint32 DstX = 0;
|
|
942 |
Uint32 DstY = 0;
|
|
943 |
if (DstTexInfo.pAtlasSuballocation)
|
|
944 |
{
|
|
945 |
const auto& Origin = DstTexInfo.pAtlasSuballocation->GetOrigin();
|
|
946 |
|
|
947 |
DstX = Origin.x;
|
|
948 |
DstY = Origin.y;
|
|
949 |
}
|
915 | 950 |
VERIFY_EXPR(Levels.size() == 1 || Levels.size() == pTexture->GetDesc().MipLevels);
|
916 | 951 |
for (Uint32 mip = 0; mip < Levels.size(); ++mip)
|
917 | 952 |
{
|
918 | 953 |
const auto& Level = Levels[mip];
|
919 | 954 |
|
|
955 |
Box UpdateBox;
|
|
956 |
UpdateBox.MinX = DstX >> mip;
|
|
957 |
UpdateBox.MaxX = UpdateBox.MinX + Level.Width;
|
|
958 |
UpdateBox.MinY = DstY >> mip;
|
|
959 |
UpdateBox.MaxY = UpdateBox.MinY + Level.Height;
|
920 | 960 |
TextureSubResData SubresData{Level.Data.data(), Level.Stride};
|
921 | |
pCtx->UpdateTexture(pTexture, mip, DstSlice, Level.UpdateBox, SubresData, RESOURCE_STATE_TRANSITION_MODE_NONE, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
|
|
961 |
pCtx->UpdateTexture(pTexture, mip, DstSlice, UpdateBox, SubresData, RESOURCE_STATE_TRANSITION_MODE_NONE, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
|
922 | 962 |
}
|
923 | 963 |
|
924 | 964 |
if (Levels.size() == 1 && pTexture->GetDesc().MipLevels > 1)
|
925 | 965 |
{
|
926 | 966 |
pCtx->GenerateMips(pTexture->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE));
|
927 | 967 |
}
|
928 | |
|
929 | |
if (Textures[i].pTexture != nullptr)
|
930 | |
{
|
931 | |
VERIFY_EXPR(pTexture == Textures[i].pTexture);
|
932 | |
Barriers.emplace_back(StateTransitionDesc{pTexture, RESOURCE_STATE_UNKNOWN, RESOURCE_STATE_SHADER_RESOURCE, true});
|
933 | |
}
|
934 | |
}
|
935 | |
else if (TexData.pStagingTex)
|
|
968 |
}
|
|
969 |
else if (pInitData->pStagingTex)
|
936 | 970 |
{
|
937 | 971 |
CopyTextureAttribs CopyAttribs;
|
938 | |
CopyAttribs.pSrcTexture = TexData.pStagingTex;
|
|
972 |
CopyAttribs.pSrcTexture = pInitData->pStagingTex;
|
939 | 973 |
CopyAttribs.pDstTexture = pTexture;
|
940 | 974 |
CopyAttribs.SrcTextureTransitionMode = RESOURCE_STATE_TRANSITION_MODE_TRANSITION;
|
941 | 975 |
CopyAttribs.DstTextureTransitionMode = RESOURCE_STATE_TRANSITION_MODE_TRANSITION;
|
942 | 976 |
CopyAttribs.SrcSlice = 0;
|
943 | 977 |
CopyAttribs.DstSlice = DstSlice;
|
944 | 978 |
|
945 | |
if (Textures[i].pAtlasSuballocation)
|
946 | |
{
|
947 | |
const auto& Origin = Textures[i].pAtlasSuballocation->GetOrigin();
|
|
979 |
if (DstTexInfo.pAtlasSuballocation)
|
|
980 |
{
|
|
981 |
const auto& Origin = DstTexInfo.pAtlasSuballocation->GetOrigin();
|
948 | 982 |
CopyAttribs.DstX = Origin.x;
|
949 | 983 |
CopyAttribs.DstY = Origin.y;
|
950 | 984 |
}
|
951 | 985 |
const auto& DstTexDesc = pTexture->GetDesc();
|
952 | |
auto NumMipLevels = std::min(DstTexDesc.MipLevels, TexData.pStagingTex->GetDesc().MipLevels);
|
|
986 |
auto NumMipLevels = std::min(DstTexDesc.MipLevels, pInitData->pStagingTex->GetDesc().MipLevels);
|
953 | 987 |
for (Uint32 mip = 0; mip < NumMipLevels; ++mip)
|
954 | 988 |
{
|
955 | 989 |
CopyAttribs.SrcMipLevel = mip;
|
|
962 | 996 |
else
|
963 | 997 |
{
|
964 | 998 |
// Texture is already initialized
|
965 | |
continue;
|
|
999 |
}
|
|
1000 |
|
|
1001 |
if (DstTexInfo.pAtlasSuballocation)
|
|
1002 |
{
|
|
1003 |
// User data is only set when the allocation is created, so no other
|
|
1004 |
// threads can call SetUserData().
|
|
1005 |
DstTexInfo.pAtlasSuballocation->SetUserData(nullptr);
|
|
1006 |
}
|
|
1007 |
else if (DstTexInfo.pTexture)
|
|
1008 |
{
|
|
1009 |
VERIFY_EXPR(pTexture == DstTexInfo.pTexture);
|
|
1010 |
Barriers.emplace_back(StateTransitionDesc{pTexture, RESOURCE_STATE_UNKNOWN, RESOURCE_STATE_SHADER_RESOURCE, true});
|
|
1011 |
|
|
1012 |
// User data is only set when the texture is created, so no other
|
|
1013 |
// threads can call SetUserData().
|
|
1014 |
pTexture->SetUserData(nullptr);
|
966 | 1015 |
}
|
967 | 1016 |
}
|
968 | 1017 |
|