diff --git a/rts/Game/UI/MouseHandler.cpp b/rts/Game/UI/MouseHandler.cpp index 62e155622b7..a82b1dc2dc4 100644 --- a/rts/Game/UI/MouseHandler.cpp +++ b/rts/Game/UI/MouseHandler.cpp @@ -1168,6 +1168,12 @@ bool CMouseHandler::ReplaceMouseCursor( CMouseCursor newCursor = CMouseCursor(newName, hotSpot); + // a replacement that loaded no frames draws nothing, so this silently turns + // a working cursor invisible. Whether the engine should refuse it is a + // behaviour question, since content may rely on it to hide the cursor. + if (!newCursor.IsValid()) + LOG_L(L_WARNING, "[MouseHandler::%s] replacement \"%s\" for \"%s\" has no frames, the cursor will draw nothing", __func__, newName.c_str(), oldName.c_str()); + // replace here so SetCursor() operates with new CMouseCursor() object // hold on the destruction of old CMouseCursor() in this place. Otherwise bad things will happen. std::swap(loadedCursors.at(fileIt->second), newCursor); diff --git a/rts/Rendering/Textures/nv_dds.cpp b/rts/Rendering/Textures/nv_dds.cpp index a4792607e77..d055e0c30a7 100644 --- a/rts/Rendering/Textures/nv_dds.cpp +++ b/rts/Rendering/Textures/nv_dds.cpp @@ -8,11 +8,9 @@ // // Description: // -// Loads DDS images (DXTC1, DXTC3, DXTC5, RGB (888, 888X), and RGBA (8888) are -// supported) for use in OpenGL. Image is flipped when its loaded as DX images -// are stored with different coordinate system. If file has mipmaps and/or -// cubemaps then these are loaded as well. Volume textures can be loaded as -// well but they must be uncompressed. +// Loads DDS images for use in OpenGL. DXT1, DXT3, DXT5, BC4, BC5, BC7, +// RGB (888, 888X), and RGBA (8888) are supported. Images are flipped to match +// OpenGL coordinates, except BC7. Volume textures must be uncompressed. // // When multiple textures are loaded (i.e a volume or cubemap texture), // additional faces can be accessed using the array operator. @@ -349,6 +347,24 @@ bool CDDSImage::load(string filename, bool flipImage) file.Read(&ddsh.dwCaps2, tmp); file.Read(&ddsh.dwReserved2, tmp*3); + const bool hasDX10Header = ((swabDWord(ddsh.ddspf.dwFlags) & DDSF_FOURCC) && swabDWord(ddsh.ddspf.dwFourCC) == FOURCC_DX10); + DDS_HEADER_DXT10 ddsh10 = {0, 0, 0, 1, 0}; + if (hasDX10Header) + { + if (file.Read(&ddsh10.dxgiFormat, tmp) != tmp || + file.Read(&ddsh10.resourceDimension, tmp) != tmp || + file.Read(&ddsh10.miscFlag, tmp) != tmp || + file.Read(&ddsh10.arraySize, tmp) != tmp || + file.Read(&ddsh10.miscFlags2, tmp) != tmp) + return false; + + ddsh10.dxgiFormat = swabDWord(ddsh10.dxgiFormat); + ddsh10.resourceDimension = swabDWord(ddsh10.resourceDimension); + ddsh10.miscFlag = swabDWord(ddsh10.miscFlag); + ddsh10.arraySize = swabDWord(ddsh10.arraySize); + ddsh10.miscFlags2 = swabDWord(ddsh10.miscFlags2); + } + // if in VFS, read post-header data directly from buffer if (file.IsBuffered()) { fileBuf = std::move(file.GetBuffer()); @@ -374,16 +390,29 @@ bool CDDSImage::load(string filename, bool flipImage) ddsh.dwCaps1 = swabDWord(ddsh.dwCaps1); ddsh.dwCaps2 = swabDWord(ddsh.dwCaps2); + if (hasDX10Header && + (ddsh10.arraySize != 1 || + (ddsh10.resourceDimension != DX10_DIMENSION_TEXTURE2D && ddsh10.resourceDimension != DX10_DIMENSION_TEXTURE3D) || + ((ddsh10.miscFlag & DX10_MISC_TEXTURECUBE) && ddsh10.resourceDimension != DX10_DIMENSION_TEXTURE2D))) + return false; + // default to flat texture type (1D, 2D, or rectangle) m_type = TextureFlat; - // check if image is a cubemap - if (ddsh.dwCaps2 & DDSF_CUBEMAP) - m_type = TextureCubemap; - - // check if image is a volume texture - if ((ddsh.dwCaps2 & DDSF_VOLUME) && (ddsh.dwDepth > 0)) - m_type = Texture3D; + if (hasDX10Header) { + if (ddsh10.miscFlag & DX10_MISC_TEXTURECUBE) + m_type = TextureCubemap; + else if (ddsh10.resourceDimension == DX10_DIMENSION_TEXTURE3D) + m_type = Texture3D; + } else { + // check if image is a cubemap + if (ddsh.dwCaps2 & DDSF_CUBEMAP) + m_type = TextureCubemap; + + // check if image is a volume texture + if ((ddsh.dwCaps2 & DDSF_VOLUME) && (ddsh.dwDepth > 0)) + m_type = Texture3D; + } // figure out what the image format is if (ddsh.ddspf.dwFlags & DDSF_FOURCC) @@ -402,6 +431,51 @@ bool CDDSImage::load(string filename, bool flipImage) m_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; m_components = 4; break; + case FOURCC_ATI1: + case FOURCC_BC4U: + m_format = GL_COMPRESSED_RED_RGTC1; + m_components = 1; + break; + case FOURCC_ATI2: + case FOURCC_BC5U: + m_format = GL_COMPRESSED_RG_RGTC2; + m_components = 2; + break; + case FOURCC_DX10: + switch (ddsh10.dxgiFormat) + { + case DXGI_FORMAT_BC1_UNORM: + m_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + m_components = 3; + break; + case DXGI_FORMAT_BC2_UNORM: + m_format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + m_components = 4; + break; + case DXGI_FORMAT_BC3_UNORM: + m_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + m_components = 4; + break; + case DXGI_FORMAT_BC4_UNORM: + m_format = GL_COMPRESSED_RED_RGTC1; + m_components = 1; + break; + case DXGI_FORMAT_BC5_UNORM: + m_format = GL_COMPRESSED_RG_RGTC2; + m_components = 2; + break; + case DXGI_FORMAT_BC7_UNORM: + m_format = GL_COMPRESSED_RGBA_BPTC_UNORM; + m_components = 4; + break; + case DXGI_FORMAT_BC7_UNORM_SRGB: + m_format = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM; + m_components = 4; + break; + default: + return false; + } + break; default: //fclose(fp); return false; @@ -435,6 +509,11 @@ bool CDDSImage::load(string filename, bool flipImage) return false; } + if (flipImage && (m_format == GL_COMPRESSED_RGBA_BPTC_UNORM || m_format == GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM)) { + LOG_L(L_WARNING, "[nv_dds] cannot vertically flip BC7 image \"%s\", author it pre-flipped", filename.c_str()); + flipImage = false; + } + // store primary surface width/height/depth unsigned int width = ddsh.dwWidth; unsigned int height = ddsh.dwHeight; @@ -578,6 +657,12 @@ bool CDDSImage::save(std::string filename, bool flipImage) const assert(m_valid); assert(m_type != TextureNone); + if (is_compressed() && + m_format != GL_COMPRESSED_RGBA_S3TC_DXT1_EXT && + m_format != GL_COMPRESSED_RGBA_S3TC_DXT3_EXT && + m_format != GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) + return false; + DDS_HEADER ddsh; unsigned int headerSize = sizeof(DDS_HEADER); memset(&ddsh, 0, headerSize); @@ -727,7 +812,11 @@ bool CDDSImage::is_compressed() const RECOIL_DETAILED_TRACY_ZONE; return ((m_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) || (m_format == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT) || - (m_format == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)); + (m_format == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) || + (m_format == GL_COMPRESSED_RED_RGTC1) || + (m_format == GL_COMPRESSED_RG_RGTC2) || + (m_format == GL_COMPRESSED_RGBA_BPTC_UNORM) || + (m_format == GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM)); } #ifndef HEADLESS @@ -969,7 +1058,7 @@ inline unsigned int CDDSImage::size_dxtc(unsigned int width, unsigned int height { RECOIL_DETAILED_TRACY_ZONE; return ((width+3)/4)*((height+3)/4)* - (m_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? 8 : 16); + ((m_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || m_format == GL_COMPRESSED_RED_RGTC1) ? 8 : 16); } /////////////////////////////////////////////////////////////////////////////// @@ -1030,6 +1119,14 @@ void CDDSImage::flip(CSurface &surface) const blocksize = 16; flipblocks = &CDDSImage::flip_blocks_dxtc5; break; + case GL_COMPRESSED_RED_RGTC1: + blocksize = 8; + flipblocks = &CDDSImage::flip_blocks_bc4; + break; + case GL_COMPRESSED_RG_RGTC2: + blocksize = 16; + flipblocks = &CDDSImage::flip_blocks_bc5; + break; default: return; } @@ -1219,6 +1316,40 @@ void CDDSImage::flip_blocks_dxtc5(DXTColBlock *line, unsigned int numBlocks) con } } +/////////////////////////////////////////////////////////////////////////////// +// flip a line of BC4 blocks +void CDDSImage::flip_blocks_bc4(DXTColBlock *line, unsigned int numBlocks) const +{ + RECOIL_DETAILED_TRACY_ZONE; + DXT5AlphaBlock *curblock = reinterpret_cast(line); + + for (unsigned int i = 0; i < numBlocks; i++) + { + flip_dxt5_alpha(curblock); + + curblock++; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// flip a line of BC5 blocks +void CDDSImage::flip_blocks_bc5(DXTColBlock *line, unsigned int numBlocks) const +{ + RECOIL_DETAILED_TRACY_ZONE; + DXT5AlphaBlock *curblock = reinterpret_cast(line); + + for (unsigned int i = 0; i < numBlocks; i++) + { + flip_dxt5_alpha(curblock); + + curblock++; + + flip_dxt5_alpha(curblock); + + curblock++; + } +} + /////////////////////////////////////////////////////////////////////////////// // CTexture implementation /////////////////////////////////////////////////////////////////////////////// diff --git a/rts/Rendering/Textures/nv_dds.h b/rts/Rendering/Textures/nv_dds.h index 1daec4cd83e..fb80331d23e 100644 --- a/rts/Rendering/Textures/nv_dds.h +++ b/rts/Rendering/Textures/nv_dds.h @@ -51,6 +51,25 @@ namespace nv_dds const unsigned int FOURCC_DXT1 = 0x31545844; //(MAKEFOURCC('D','X','T','1')) const unsigned int FOURCC_DXT3 = 0x33545844; //(MAKEFOURCC('D','X','T','3')) const unsigned int FOURCC_DXT5 = 0x35545844; //(MAKEFOURCC('D','X','T','5')) + const unsigned int FOURCC_ATI1 = 0x31495441; //(MAKEFOURCC('A','T','I','1')), BC4 + const unsigned int FOURCC_BC4U = 0x55344342; //(MAKEFOURCC('B','C','4','U')) + const unsigned int FOURCC_ATI2 = 0x32495441; //(MAKEFOURCC('A','T','I','2')), BC5 + const unsigned int FOURCC_BC5U = 0x55354342; //(MAKEFOURCC('B','C','5','U')) + const unsigned int FOURCC_DX10 = 0x30315844; //(MAKEFOURCC('D','X','1','0')) + + // Supported DXGI formats + const unsigned int DXGI_FORMAT_BC1_UNORM = 71; + const unsigned int DXGI_FORMAT_BC2_UNORM = 74; + const unsigned int DXGI_FORMAT_BC3_UNORM = 77; + const unsigned int DXGI_FORMAT_BC4_UNORM = 80; + const unsigned int DXGI_FORMAT_BC5_UNORM = 83; + const unsigned int DXGI_FORMAT_BC7_UNORM = 98; + const unsigned int DXGI_FORMAT_BC7_UNORM_SRGB = 99; + + // DDS_HEADER_DXT10 values + const unsigned int DX10_DIMENSION_TEXTURE2D = 3; + const unsigned int DX10_DIMENSION_TEXTURE3D = 4; + const unsigned int DX10_MISC_TEXTURECUBE = 0x00000004; struct DXTColBlock { @@ -85,6 +104,15 @@ namespace nv_dds unsigned int dwABitMask; }; + struct DDS_HEADER_DXT10 + { + unsigned int dxgiFormat; + unsigned int resourceDimension; + unsigned int miscFlag; + unsigned int arraySize; + unsigned int miscFlags2; + }; + struct DDS_HEADER { unsigned int dwSize; @@ -333,6 +361,8 @@ namespace nv_dds void flip_blocks_dxtc1(DXTColBlock *line, unsigned int numBlocks) const; void flip_blocks_dxtc3(DXTColBlock *line, unsigned int numBlocks) const; void flip_blocks_dxtc5(DXTColBlock *line, unsigned int numBlocks) const; + void flip_blocks_bc4(DXTColBlock *line, unsigned int numBlocks) const; + void flip_blocks_bc5(DXTColBlock *line, unsigned int numBlocks) const; void flip_dxt5_alpha(DXT5AlphaBlock *block) const; bool write_texture(const CTexture &texture, FILE *fp) const; diff --git a/rts/Sim/Path/QTPFS/Path.h b/rts/Sim/Path/QTPFS/Path.h index 6a354b8a324..88ed9e14dc8 100644 --- a/rts/Sim/Path/QTPFS/Path.h +++ b/rts/Sim/Path/QTPFS/Path.h @@ -295,24 +295,15 @@ namespace QTPFS { points.clear(); points.resize(n); } - void CopyPoints(const IPath& p) { - AllocPoints(p.NumPoints()); - for (unsigned int n = 0; n < p.NumPoints(); n++) { - points[n] = p.GetPoint(n); - } - } + void CopyPoints(const IPath& p) { points = p.points; } + void AllocNodes(unsigned int n) { nodes.clear(); nodes.resize(n); } - void CopyNodes(const IPath& p) { - AllocNodes(p.nodes.size()); - for (unsigned int n = 0; n < p.nodes.size(); n++) { - nodes[n] = p.GetNode(n); - } - } + void CopyNodes(const IPath& p) { nodes = p.nodes; } // Function is for debugging and logging purposes only uint32_t CalculateHash() const { @@ -345,15 +336,15 @@ namespace QTPFS { spring_time GetSearchTime() const { return searchTime; } // Incomplete paths need to be rebuilt from time to time as the owner makes progress. - unsigned int GetRepathTriggerIndex() const { return repathAtPointIndex; } + uint32_t GetRepathTriggerIndex() const { return repathAtPointIndex; } void SetRepathTriggerIndex(unsigned int index) { repathAtPointIndex = index; } void ClearGetRepathTriggerIndex() { repathAtPointIndex = 0; } - float3 GetGoalPosition() const { return goalPosition; } + const float3& GetGoalPosition() const { return goalPosition; } void SetGoalPosition(float3 point) { goalPosition = point; } - unsigned int GetFirstNodeIdOfCleanPath() const { return firstNodeIdOfCleanPath; } + uint32_t GetFirstNodeIdOfCleanPath() const { return firstNodeIdOfCleanPath; } void SetFirstNodeIdOfCleanPath(int nodeId) { firstNodeIdOfCleanPath = nodeId; } bool IsRawPath() const { return isRawPath; } diff --git a/rts/Sim/Path/QTPFS/PathCache.cpp b/rts/Sim/Path/QTPFS/PathCache.cpp index 20f152006d5..bf1ae9a278b 100644 --- a/rts/Sim/Path/QTPFS/PathCache.cpp +++ b/rts/Sim/Path/QTPFS/PathCache.cpp @@ -40,9 +40,8 @@ static void GetRectangleCollisionVolume(const SRectangle& r, CollisionVolume& v, rm.z = ((r.z1 + r.z2) * SQUARE_SIZE) >> 1; rm.y = 0.0f; - #define CV CollisionVolume + using CV = CollisionVolume; v.InitShape(vScales, ZeroVector, CV::COLVOL_TYPE_BOX, CV::COLVOL_HITTEST_CONT, CV::COLVOL_AXIS_Y); - #undef CV } bool QTPFS::PathCache::MarkDeadPaths(const SRectangle& r, const NodeLayer& nodeLayer) { diff --git a/rts/Sim/Path/QTPFS/PathSearch.cpp b/rts/Sim/Path/QTPFS/PathSearch.cpp index 3f4fa65c1c6..b64130300c5 100644 --- a/rts/Sim/Path/QTPFS/PathSearch.cpp +++ b/rts/Sim/Path/QTPFS/PathSearch.cpp @@ -181,7 +181,7 @@ void QTPFS::PathSearch::InitializeThread(SearchThreadData* threadData, IPath* pa // auto *pathToRepair = ( tryPathRepair && registry.valid(QTPFS::entity(searchID)) ) // ? registry.try_get(QTPFS::entity(searchID)) : nullptr; // FIXME: race condition - // Path repairs need only search to the point of finding the beginning of the renaming clean part of the old path. + // Path repairs need only search to the point of finding the beginning of the remaining clean part of the old path. // Such searches are also restricted in the area they can search to avoid creating poor paths that would be better // off being recreated from scratch. doPathRepair = tryPathRepair diff --git a/rts/Sim/Weapons/Weapon.cpp b/rts/Sim/Weapons/Weapon.cpp index 21f304d3bf9..3424e1d5f5a 100644 --- a/rts/Sim/Weapons/Weapon.cpp +++ b/rts/Sim/Weapons/Weapon.cpp @@ -819,6 +819,12 @@ void CWeapon::HoldIfTargetInvalid() return; if (!TryTarget(currentTarget)) { + // BombDroppers must retain ground targets until their active salvo ends. + // Dropping one after the aircraft passes the target prevents the CAI from + // associating the completed salvo with its current attack command. + if (noAutoTarget && HavePosTarget() && salvoLeft > 0) + return; + DropCurrentTarget(); return; } diff --git a/rts/System/Threading/ThreadPool.h b/rts/System/Threading/ThreadPool.h index ee5251511f0..48fdc10acce 100644 --- a/rts/System/Threading/ThreadPool.h +++ b/rts/System/Threading/ThreadPool.h @@ -224,7 +224,10 @@ class ITaskGroup } uint32_t GetId() const { return id; } - uint64_t GetDeltaTime(const spring_time t) const { return (std::max(ts.load(), uint64_t(t.toNanoSecsi())) - ts); } + uint64_t GetDeltaTime(const spring_time t0) const { + const uint64_t t1 = ts.load(); + return (std::max(t1, uint64_t(t0.toNanoSecsi())) - t1); + } void UpdateId() { id = lastId.fetch_add(1); } void SetTimeStamp(const spring_time t) { ts = t.toNanoSecsi(); }