[Scummvm-git-logs] scummvm master -> 02085d54819aaae3c2f9a7f1148b1814d162b5c6

criezy noreply at scummvm.org
Sun Jun 12 23:30:57 UTC 2022


This automated email contains information about 7 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
29ee2d2ab8 AGS: Separated texture data from DDB objects with render settings
487a58b3a8 AGS: Implements short-term texture cache in graphic renderers
7382a1a551 AGS: Game objects use texture cache when possible
aecb62429d AGS: Update shared texture when dynamic sprite changes
370e939ad7 AGS: Fixes for MemoryStream: proper seek support for writing mode
e26adb919e AGS: Moved global DlgOpt into show_dialog_options() function
02085d5481 AGS: Refactored do_conversation()


Commit: 29ee2d2ab8711c3dac94340b9c7e02d10936901d
    https://github.com/scummvm/scummvm/commit/29ee2d2ab8711c3dac94340b9c7e02d10936901d
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2022-06-13T00:30:21+01:00

Commit Message:
AGS: Separated texture data from DDB objects with render settings

This allows to have multiple DDBs sharing same texture data.
>From upstream fbcb62c507fb15ffc288d5ebdc1f0d664e66ecb8

Changed paths:
    engines/ags/engine/gfx/ali_3d_scummvm.h
    engines/ags/engine/gfx/ddb.h
    engines/ags/engine/gfx/gfx_driver_base.h


diff --git a/engines/ags/engine/gfx/ali_3d_scummvm.h b/engines/ags/engine/gfx/ali_3d_scummvm.h
index 423242e62a7..e882433dcd4 100644
--- a/engines/ags/engine/gfx/ali_3d_scummvm.h
+++ b/engines/ags/engine/gfx/ali_3d_scummvm.h
@@ -107,13 +107,7 @@ public:
 		return _stretchToHeight;
 	}
 
-	void Dispose() {
-		// do we want to free the bitmap?
-	}
-
-	~ALSoftwareBitmap() override {
-		Dispose();
-	}
+	~ALSoftwareBitmap() override = default;
 };
 
 
diff --git a/engines/ags/engine/gfx/ddb.h b/engines/ags/engine/gfx/ddb.h
index 804e73219fc..ac300df24db 100644
--- a/engines/ags/engine/gfx/ddb.h
+++ b/engines/ags/engine/gfx/ddb.h
@@ -21,12 +21,12 @@
 
 //=============================================================================
 //
-// Driver-dependant bitmap interface
+// Driver-dependant bitmap interface.
 //
-// TODO: split into texture object that has only tex data
-// and object describing a drawing operation, with ref to texture and
-// drawing parameters (modes, shaders, etc).
-// Then we will also be able to share one texture among multiple game entities.
+// This interface describes an individual sprite object. The actual texture
+// data (pixel data) may be shared among multiple DDBs, while DDB define
+// additional settings telling how to present the texture: transform, colorize,
+// and so on.
 //=============================================================================
 
 #ifndef AGS_ENGINE_GFX_DDB_H
diff --git a/engines/ags/engine/gfx/gfx_driver_base.h b/engines/ags/engine/gfx/gfx_driver_base.h
index 35b62a657a2..60ba9069070 100644
--- a/engines/ags/engine/gfx/gfx_driver_base.h
+++ b/engines/ags/engine/gfx/gfx_driver_base.h
@@ -28,6 +28,7 @@
 #ifndef AGS_ENGINE_GFX_GFX_DRIVER_BASE_H
 #define AGS_ENGINE_GFX_GFX_DRIVER_BASE_H
 
+#include "ags/lib/std/memory.h"
 #include "ags/lib/std/vector.h"
 #include "ags/engine/gfx/ddb.h"
 #include "ags/engine/gfx/graphics_driver.h"
@@ -169,11 +170,6 @@ protected:
 
 
 
-// Generic TextureTile base
-struct TextureTile {
-	int x, y;
-	int width, height;
-};
 
 // Parent class for the video memory DDBs
 class BaseDDB : public IDriverDependantBitmap {
@@ -198,6 +194,22 @@ protected:
 	virtual ~BaseDDB() {}
 };
 
+// A base parent for the otherwise opaque texture data object;
+// TextureData refers to the pixel data itself, with no additional
+// properties. It may be shared between multiple sprites if necessary.
+struct TextureData {
+	virtual ~TextureData() = default;
+protected:
+	TextureData() = default;
+};
+
+// Generic TextureTile base
+struct TextureTile {
+	int x = 0, y = 0;
+	int width = 0, height = 0;
+};
+
+
 // VideoMemoryGraphicsDriver - is the parent class for the graphic drivers
 // which drawing method is based on passing the sprite stack into GPU,
 // rather than blitting to flat screen bitmap.
@@ -211,9 +223,20 @@ public:
 	void SetMemoryBackBuffer(Bitmap *backBuffer) override;
 	Bitmap *GetStageBackBuffer(bool mark_dirty) override;
 	bool GetStageMatrixes(RenderMatrixes &rm) override;
+	IDriverDependantBitmap *CreateDDB(int width, int height, int color_depth, bool opaque) = 0;
 	IDriverDependantBitmap *CreateDDBFromBitmap(Bitmap *bitmap, bool hasAlpha, bool opaque = false) override;
 
 protected:
+	// Create texture data with the given parameters
+	virtual TextureData *CreateTextureData(int width, int height, bool opaque) = 0;
+	// Update texture data from the given bitmap
+	virtual void UpdateTextureData(TextureData *txdata, Bitmap *bmp, bool opaque, bool hasAlpha) = 0;
+	// Create DDB using preexisting texture data
+	virtual IDriverDependantBitmap *CreateDDB(std::shared_ptr<TextureData> txdata,
+		  int width, int height, int color_depth, bool opaque) = 0;
+	// Retrieve shared texture data object from the given DDB
+	virtual std::shared_ptr<TextureData> GetTextureData(IDriverDependantBitmap *ddb) = 0;
+
 	// Stage screens are raw bitmap buffers meant to be sent to plugins on demand
 	// at certain drawing stages. If used at least once these buffers are then
 	// rendered as additional sprites in their respected order.


Commit: 487a58b3a8b1971bebcec12e16d88ab017efadd2
    https://github.com/scummvm/scummvm/commit/487a58b3a8b1971bebcec12e16d88ab017efadd2
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2022-06-13T00:30:21+01:00

Commit Message:
AGS: Implements short-term texture cache in graphic renderers

- caches textures while they are in the immediate use;
- this lets to share same texture data among multiple sprites on screen.
- texture entries are identified by an arbitrary uint32 number.

>From upstream 6f3f84e049df2f800aa4b06d221a393d904c2826

Changed paths:
    engines/ags/engine/gfx/ali_3d_scummvm.h
    engines/ags/engine/gfx/ddb.h
    engines/ags/engine/gfx/gfx_driver_base.cpp
    engines/ags/engine/gfx/gfx_driver_base.h
    engines/ags/engine/gfx/graphics_driver.h


diff --git a/engines/ags/engine/gfx/ali_3d_scummvm.h b/engines/ags/engine/gfx/ali_3d_scummvm.h
index e882433dcd4..23f28d00576 100644
--- a/engines/ags/engine/gfx/ali_3d_scummvm.h
+++ b/engines/ags/engine/gfx/ali_3d_scummvm.h
@@ -59,6 +59,8 @@ enum RendererFlip {
 
 class ALSoftwareBitmap : public BaseDDB {
 public:
+	uint32_t GetRefID() const override { return UINT32_MAX /* not supported */; }
+
 	int  GetAlpha() const override {
 		return _alpha;
 	}
@@ -177,6 +179,12 @@ public:
 	void UpdateDDBFromBitmap(IDriverDependantBitmap *ddb, Bitmap *bitmap, bool hasAlpha) override;
 	void DestroyDDB(IDriverDependantBitmap *ddb) override;
 
+	IDriverDependantBitmap *GetSharedDDB(uint32_t sprite_id,
+		Bitmap *bitmap, bool hasAlpha, bool opaque) override {
+		// Software renderer does not require a texture cache, because it uses bitmaps directly
+		return CreateDDBFromBitmap(bitmap, hasAlpha, opaque);
+	}
+
 	void DrawSprite(int x, int y, IDriverDependantBitmap *ddb) override;
 	void SetScreenFade(int red, int green, int blue) override;
 	void SetScreenTint(int red, int green, int blue) override;
diff --git a/engines/ags/engine/gfx/ddb.h b/engines/ags/engine/gfx/ddb.h
index ac300df24db..c6f1f6865d8 100644
--- a/engines/ags/engine/gfx/ddb.h
+++ b/engines/ags/engine/gfx/ddb.h
@@ -38,6 +38,9 @@ namespace Engine {
 
 class IDriverDependantBitmap {
 public:
+	// Get an arbitrary sprite ID, returns UINT32_MAX if does not have one
+	virtual uint32_t GetRefID() const = 0;
+
 	virtual int  GetAlpha() const = 0;
 	virtual void SetAlpha(int alpha) = 0;  // 0-255
 	virtual void SetFlippedLeftRight(bool isFlipped) = 0;
diff --git a/engines/ags/engine/gfx/gfx_driver_base.cpp b/engines/ags/engine/gfx/gfx_driver_base.cpp
index a74ba4e15c8..601d5e8b46f 100644
--- a/engines/ags/engine/gfx/gfx_driver_base.cpp
+++ b/engines/ags/engine/gfx/gfx_driver_base.cpp
@@ -173,6 +173,35 @@ IDriverDependantBitmap *VideoMemoryGraphicsDriver::CreateDDBFromBitmap(Bitmap *b
 	return ddb;
 }
 
+IDriverDependantBitmap *VideoMemoryGraphicsDriver::GetSharedDDB(uint32_t sprite_id, Bitmap *bitmap, bool hasAlpha, bool opaque) {
+	const auto found = _txRefs.find(sprite_id);
+	if (found != _txRefs.end()) {
+		const auto &item = found->_value;
+		if (!item.Data.expired())
+			return CreateDDB(item.Data.lock(), item.Res.Width, item.Res.Height, item.Res.ColorDepth, opaque);
+	}
+
+	// Create and add a new element
+	std::shared_ptr<TextureData> txdata(CreateTextureData(bitmap->GetWidth(), bitmap->GetHeight(), opaque));
+	txdata->ID = sprite_id;
+	UpdateTextureData(txdata.get(), bitmap, opaque, hasAlpha);
+	// only add into the map when has valid sprite ID
+	if (sprite_id != UINT32_MAX) {
+		_txRefs[sprite_id] = TextureCacheItem(txdata,
+			GraphicResolution(bitmap->GetWidth(), bitmap->GetHeight(), bitmap->GetColorDepth()));
+	}
+	return CreateDDB(txdata, bitmap->GetWidth(), bitmap->GetHeight(), bitmap->GetColorDepth(), opaque);
+}
+
+ void VideoMemoryGraphicsDriver::DestroyDDB(IDriverDependantBitmap* ddb) {
+	uint32_t sprite_id = ddb->GetRefID();
+	DestroyDDBImpl(ddb);
+	// Remove shared object from ref list if no more active refs left
+	const auto found = _txRefs.find(sprite_id);
+	if (found != _txRefs.end() && found->_value.Data.expired())
+		_txRefs.erase(found);
+}
+
 PBitmap VideoMemoryGraphicsDriver::CreateStageScreen(size_t index, const Size &sz) {
 	if (_stageScreens.size() <= index)
 		_stageScreens.resize(index + 1);
diff --git a/engines/ags/engine/gfx/gfx_driver_base.h b/engines/ags/engine/gfx/gfx_driver_base.h
index 60ba9069070..14b5842ec6a 100644
--- a/engines/ags/engine/gfx/gfx_driver_base.h
+++ b/engines/ags/engine/gfx/gfx_driver_base.h
@@ -29,6 +29,7 @@
 #define AGS_ENGINE_GFX_GFX_DRIVER_BASE_H
 
 #include "ags/lib/std/memory.h"
+#include "engines/ags/lib/std/map.h"
 #include "ags/lib/std/vector.h"
 #include "ags/engine/gfx/ddb.h"
 #include "ags/engine/gfx/graphics_driver.h"
@@ -198,6 +199,7 @@ protected:
 // TextureData refers to the pixel data itself, with no additional
 // properties. It may be shared between multiple sprites if necessary.
 struct TextureData {
+	uint32_t ID = UINT32_MAX;
 	virtual ~TextureData() = default;
 protected:
 	TextureData() = default;
@@ -223,8 +225,11 @@ public:
 	void SetMemoryBackBuffer(Bitmap *backBuffer) override;
 	Bitmap *GetStageBackBuffer(bool mark_dirty) override;
 	bool GetStageMatrixes(RenderMatrixes &rm) override;
-	IDriverDependantBitmap *CreateDDB(int width, int height, int color_depth, bool opaque) = 0;
+	IDriverDependantBitmap *CreateDDB(int width, int height, int color_depth, bool opaque) override = 0;
 	IDriverDependantBitmap *CreateDDBFromBitmap(Bitmap *bitmap, bool hasAlpha, bool opaque = false) override;
+	// Get shared texture from cache, or create from bitmap and assign ID
+	IDriverDependantBitmap *GetSharedDDB(uint32_t sprite_id, Bitmap *bitmap, bool hasAlpha, bool opaque) override;
+	void DestroyDDB(IDriverDependantBitmap* ddb) override;
 
 protected:
 	// Create texture data with the given parameters
@@ -236,6 +241,7 @@ protected:
 		  int width, int height, int color_depth, bool opaque) = 0;
 	// Retrieve shared texture data object from the given DDB
 	virtual std::shared_ptr<TextureData> GetTextureData(IDriverDependantBitmap *ddb) = 0;
+	virtual void DestroyDDBImpl(IDriverDependantBitmap* ddb) = 0;
 
 	// Stage screens are raw bitmap buffers meant to be sent to plugins on demand
 	// at certain drawing stages. If used at least once these buffers are then
@@ -293,6 +299,20 @@ private:
 	};
 	std::vector<ScreenFx> _fxPool;
 	size_t _fxIndex; // next free pool item
+
+	// Texture short-term cache:
+	// - caches textures while they are in the immediate use;
+	// - this lets to share same texture data among multiple sprites on screen.
+	// TextureCacheItem stores weak references to the existing texture tiles,
+	// identified by an arbitrary uint32 number.
+	struct TextureCacheItem {
+		GraphicResolution Res;
+		std::weak_ptr<TextureData> Data;
+		TextureCacheItem() = default;
+		TextureCacheItem(std::shared_ptr<TextureData> data, const GraphicResolution &res)
+			: Data(data), Res(res) {}
+	};
+	std::unordered_map<uint32_t, TextureCacheItem> _txRefs;
 };
 
 } // namespace Engine
diff --git a/engines/ags/engine/gfx/graphics_driver.h b/engines/ags/engine/gfx/graphics_driver.h
index b893ca31da1..b3d04ea3e6b 100644
--- a/engines/ags/engine/gfx/graphics_driver.h
+++ b/engines/ags/engine/gfx/graphics_driver.h
@@ -139,6 +139,10 @@ public:
 	virtual void UpdateDDBFromBitmap(IDriverDependantBitmap *bitmapToUpdate, Shared::Bitmap *bitmap, bool hasAlpha) = 0;
 	virtual void DestroyDDB(IDriverDependantBitmap *bitmap) = 0;
 
+	// Get shared texture from cache, or create from bitmap and assign ID
+	virtual IDriverDependantBitmap *GetSharedDDB(uint32_t sprite_id,
+		Shared::Bitmap *bitmap = nullptr, bool hasAlpha = true, bool opaque = false) = 0;
+
 	// Prepares next sprite batch, a list of sprites with defined viewport and optional
 	// global model transformation; all subsequent calls to DrawSprite will be adding
 	// sprites to this batch's list.


Commit: 7382a1a551246f3cb85555de532b08343c2b62a9
    https://github.com/scummvm/scummvm/commit/7382a1a551246f3cb85555de532b08343c2b62a9
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2022-06-13T00:30:21+01:00

Commit Message:
AGS: Game objects use texture cache when possible

Specify sprite ID for shared textures for: characters, objects, overlays
GUI and GUI objects are more complicated, a they may contain custom
(generated) graphics; using shared textures for them would require a
bigger change in how their sprites are organized when gathering draw
lists for render.

>From upstream b724b43fa7f13ea2e89a3e9213dd76b00e59f9f4

Changed paths:
    engines/ags/engine/ac/draw.cpp
    engines/ags/engine/ac/draw.h


diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index 5660172d894..7985020eb4a 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -766,22 +766,27 @@ void draw_sprite_slot_support_alpha(Bitmap *ds, bool ds_has_alpha, int xpos, int
 	                          blend_mode, alpha);
 }
 
-IDriverDependantBitmap *recycle_ddb_bitmap(IDriverDependantBitmap *ddb, Bitmap *source, bool has_alpha, bool opaque) {
-	if (ddb) {
-		// same colour depth, width and height -> reuse
-		if ((ddb->GetColorDepth() == source->GetColorDepth()) &&
-			(ddb->GetWidth() == source->GetWidth()) && (ddb->GetHeight() == source->GetHeight())) {
-			_G(gfxDriver)->UpdateDDBFromBitmap(ddb, source, has_alpha);
-			return ddb;
-		}
-
-		_G(gfxDriver)->DestroyDDB(ddb);
-	}
-	return _G(gfxDriver)->CreateDDBFromBitmap(source, has_alpha, opaque);
+Engine::IDriverDependantBitmap* recycle_ddb_sprite(Engine::IDriverDependantBitmap *ddb, int sprite_id, Shared::Bitmap *source, bool has_alpha, bool opaque) {
+	// no ddb, - get or create shared object
+	if (!ddb)
+		return _G(gfxDriver)->GetSharedDDB(sprite_id, source, has_alpha, opaque);
+	// same sprite id, - use existing
+	if ((sprite_id != UINT32_MAX) && (ddb->GetRefID() == sprite_id))
+		return ddb;
+	// not related to a sprite ID, but has same resolution, -
+	// repaint directly from the given bitmap
+	if ((sprite_id == UINT32_MAX) && (ddb->GetColorDepth() == source->GetColorDepth()) &&
+		(ddb->GetWidth() == source->GetWidth()) && (ddb->GetHeight() == source->GetHeight())) {
+		_G(gfxDriver)->UpdateDDBFromBitmap(ddb, source, has_alpha);
+		return ddb;
+	}
+	// have to recreate ddb
+	_G(gfxDriver)->DestroyDDB(ddb);
+	return _G(gfxDriver)->GetSharedDDB(sprite_id, source, has_alpha, opaque);
 }
 
 void sync_object_texture(ObjTexture &obj, bool has_alpha = false, bool opaque = false) {
-	obj.Ddb = recycle_ddb_bitmap(obj.Ddb, obj.Bmp.get(), has_alpha, opaque);
+	obj.Ddb = recycle_ddb_sprite(obj.Ddb, obj.SpriteID, obj.Bmp.get(), has_alpha, opaque);
 }
 
 //------------------------------------------------------------------------
@@ -1211,6 +1216,7 @@ int construct_object_gfx(int aa, int *drawnWidth, int *drawnHeight, bool alwaysU
 	}
 
 	auto &actsp = _GP(actsps)[useindx];
+	actsp.SpriteID = _G(objs)[aa].num; // for texture sharing
 	if ((hardwareAccelerated) &&
 			(_G(walkBehindMethod) != DrawOverCharSprite) &&
 			(_G(objcache)[aa].image != nullptr) &&
@@ -1507,6 +1513,7 @@ void prepare_characters_for_drawing() {
 		_G(our_eip) = 3331;
 
 		auto &actsp = _GP(actsps)[useindx];
+		actsp.SpriteID = sppic; // for texture sharing
 
 		// if the character was the same sprite and scaling last time,
 		// just use the cached image
@@ -2127,7 +2134,7 @@ static void construct_overlays() {
 				use_bmp = _GP(overlaybmp)[i].get();
 			}
 
-			over.ddb = recycle_ddb_bitmap(over.ddb, use_bmp, over.HasAlphaChannel());
+			over.ddb = recycle_ddb_sprite(over.ddb, over.GetSpriteNum(), use_bmp, over.HasAlphaChannel());
 			over.ClearChanged();
 		}
 
diff --git a/engines/ags/engine/ac/draw.h b/engines/ags/engine/ac/draw.h
index 6c032b51edb..426602011d6 100644
--- a/engines/ags/engine/ac/draw.h
+++ b/engines/ags/engine/ac/draw.h
@@ -63,6 +63,8 @@ struct RoomCameraDrawData {
 // ObjTexture is a helper struct that pairs a raw bitmap with
 // a renderer's texture and an optional position
 struct ObjTexture {
+	// Sprite ID
+	uint32_t SpriteID = UINT32_MAX;
 	// Raw bitmap
 	std::unique_ptr<Shared::Bitmap> Bmp;
 	// Corresponding texture, created by renderer
@@ -149,7 +151,10 @@ void mark_current_background_dirty();
 // Avoid freeing and reallocating the memory if possible
 Shared::Bitmap *recycle_bitmap(Shared::Bitmap *bimp, int coldep, int wid, int hit, bool make_transparent = false);
 void recycle_bitmap(std::unique_ptr<Shared::Bitmap> &bimp, int coldep, int wid, int hit, bool make_transparent = false);
-Engine::IDriverDependantBitmap *recycle_ddb_bitmap(Engine::IDriverDependantBitmap *ddb, Shared::Bitmap *source, bool has_alpha = false, bool opaque = false);
+Engine::IDriverDependantBitmap* recycle_ddb_sprite(Engine::IDriverDependantBitmap *ddb, int sprite_id, Shared::Bitmap *source, bool has_alpha = false, bool opaque = false);
+inline Engine::IDriverDependantBitmap* recycle_ddb_bitmap(Engine::IDriverDependantBitmap *ddb, Shared::Bitmap *source, bool has_alpha = false, bool opaque = false) {
+	return recycle_ddb_sprite(ddb, UINT32_MAX, source, has_alpha, opaque);
+}
 // Draw everything
 void render_graphics(Engine::IDriverDependantBitmap *extraBitmap = nullptr, int extraX = 0, int extraY = 0);
 // Construct game scene, scheduling drawing list for the renderer


Commit: aecb62429d25a64f48c77d745450105092f363fd
    https://github.com/scummvm/scummvm/commit/aecb62429d25a64f48c77d745450105092f363fd
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2022-06-13T00:30:21+01:00

Commit Message:
AGS: Update shared texture when dynamic sprite changes

>From upstream 6be0e6d094e70e96d93a9ee4dd31ca166663eeaf

Changed paths:
    engines/ags/engine/ac/game.cpp
    engines/ags/engine/gfx/ali_3d_scummvm.h
    engines/ags/engine/gfx/gfx_driver_base.cpp
    engines/ags/engine/gfx/gfx_driver_base.h
    engines/ags/engine/gfx/graphics_driver.h


diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index f5818ca7dff..dc8202979e6 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -1340,6 +1340,9 @@ bool unserialize_audio_script_object(int index, const char *objectType, Stream *
 }
 
 void game_sprite_updated(int sprnum) {
+	// update the shared texture (if exists)
+	_G(gfxDriver)->UpdateSharedDDB(sprnum, _GP(spriteset)[sprnum], _GP(game).SpriteInfos[sprnum].Flags & SPF_ALPHACHANNEL, false);
+
 	// character and object draw caches
 	reset_objcache_for_sprite(sprnum);
 
@@ -1369,6 +1372,8 @@ void game_sprite_updated(int sprnum) {
 }
 
 void game_sprite_deleted(int sprnum) {
+	// clear from texture cache
+	_G(gfxDriver)->ClearSharedDDB(sprnum);
 	// character and object draw caches
 	reset_objcache_for_sprite(sprnum);
 	// room object graphics
diff --git a/engines/ags/engine/gfx/ali_3d_scummvm.h b/engines/ags/engine/gfx/ali_3d_scummvm.h
index 23f28d00576..3a23c37535e 100644
--- a/engines/ags/engine/gfx/ali_3d_scummvm.h
+++ b/engines/ags/engine/gfx/ali_3d_scummvm.h
@@ -185,6 +185,13 @@ public:
 		return CreateDDBFromBitmap(bitmap, hasAlpha, opaque);
 	}
 
+	void UpdateSharedDDB(uint32_t /*sprite_id*/, Bitmap */*bitmap*/, bool /*hasAlpha*/, bool /*opaque*/) override {
+		/* do nothing */
+	}
+	void ClearSharedDDB(uint32_t /*sprite_id*/) override {
+		/* do nothing */
+	}
+
 	void DrawSprite(int x, int y, IDriverDependantBitmap *ddb) override;
 	void SetScreenFade(int red, int green, int blue) override;
 	void SetScreenTint(int red, int green, int blue) override;
diff --git a/engines/ags/engine/gfx/gfx_driver_base.cpp b/engines/ags/engine/gfx/gfx_driver_base.cpp
index 601d5e8b46f..6efab7c00ae 100644
--- a/engines/ags/engine/gfx/gfx_driver_base.cpp
+++ b/engines/ags/engine/gfx/gfx_driver_base.cpp
@@ -193,6 +193,21 @@ IDriverDependantBitmap *VideoMemoryGraphicsDriver::GetSharedDDB(uint32_t sprite_
 	return CreateDDB(txdata, bitmap->GetWidth(), bitmap->GetHeight(), bitmap->GetColorDepth(), opaque);
 }
 
+void VideoMemoryGraphicsDriver::UpdateSharedDDB(uint32_t sprite_id, Bitmap *bitmap, bool hasAlpha, bool opaque) {
+	const auto found = _txRefs.find(sprite_id);
+	if (found != _txRefs.end()) {
+		auto txdata = found->_value.Data.lock();
+		if (txdata)
+			UpdateTextureData(txdata.get(), bitmap, opaque, hasAlpha);
+	}
+ }
+
+ void VideoMemoryGraphicsDriver::ClearSharedDDB(uint32_t sprite_id) {
+	const auto found = _txRefs.find(sprite_id);
+	if (found != _txRefs.end())
+		_txRefs.erase(found);
+ }
+
  void VideoMemoryGraphicsDriver::DestroyDDB(IDriverDependantBitmap* ddb) {
 	uint32_t sprite_id = ddb->GetRefID();
 	DestroyDDBImpl(ddb);
diff --git a/engines/ags/engine/gfx/gfx_driver_base.h b/engines/ags/engine/gfx/gfx_driver_base.h
index 14b5842ec6a..e178ddcf1bf 100644
--- a/engines/ags/engine/gfx/gfx_driver_base.h
+++ b/engines/ags/engine/gfx/gfx_driver_base.h
@@ -229,6 +229,10 @@ public:
 	IDriverDependantBitmap *CreateDDBFromBitmap(Bitmap *bitmap, bool hasAlpha, bool opaque = false) override;
 	// Get shared texture from cache, or create from bitmap and assign ID
 	IDriverDependantBitmap *GetSharedDDB(uint32_t sprite_id, Bitmap *bitmap, bool hasAlpha, bool opaque) override;
+	// Removes the shared texture reference, will force the texture to recreate next time
+	 void ClearSharedDDB(uint32_t sprite_id) override;
+	 // Updates shared texture data, but only if it is present in the cache
+	 void UpdateSharedDDB(uint32_t sprite_id, Bitmap *bitmap, bool hasAlpha, bool opaque) override;
 	void DestroyDDB(IDriverDependantBitmap* ddb) override;
 
 protected:
diff --git a/engines/ags/engine/gfx/graphics_driver.h b/engines/ags/engine/gfx/graphics_driver.h
index b3d04ea3e6b..da1371827be 100644
--- a/engines/ags/engine/gfx/graphics_driver.h
+++ b/engines/ags/engine/gfx/graphics_driver.h
@@ -142,6 +142,9 @@ public:
 	// Get shared texture from cache, or create from bitmap and assign ID
 	virtual IDriverDependantBitmap *GetSharedDDB(uint32_t sprite_id,
 		Shared::Bitmap *bitmap = nullptr, bool hasAlpha = true, bool opaque = false) = 0;
+	virtual void UpdateSharedDDB(uint32_t sprite_id, Shared::Bitmap *bitmap = nullptr, bool hasAlpha = true, bool opaque = false) = 0;
+	// Removes the shared texture reference, will force the texture to recreate next time
+	virtual void ClearSharedDDB(uint32_t sprite_id) = 0;
 
 	// Prepares next sprite batch, a list of sprites with defined viewport and optional
 	// global model transformation; all subsequent calls to DrawSprite will be adding


Commit: 370e939ad79bd1306a00256bbdc2aeb68fafbce0
    https://github.com/scummvm/scummvm/commit/370e939ad79bd1306a00256bbdc2aeb68fafbce0
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2022-06-13T00:30:21+01:00

Commit Message:
AGS: Fixes for MemoryStream: proper seek support for writing mode

>From upstream 80a17c94f6fc44955dcee5c559247ea73a8c0a76

Changed paths:
    engines/ags/shared/util/memory_stream.cpp
    engines/ags/shared/util/memory_stream.h


diff --git a/engines/ags/shared/util/memory_stream.cpp b/engines/ags/shared/util/memory_stream.cpp
index 69bcd01f1bd..08fb485afc9 100644
--- a/engines/ags/shared/util/memory_stream.cpp
+++ b/engines/ags/shared/util/memory_stream.cpp
@@ -31,19 +31,25 @@ MemoryStream::MemoryStream(const uint8_t *cbuf, size_t buf_sz, DataEndianess str
 	, _cbuf(cbuf)
 	, _buf_sz(buf_sz)
 	, _len(buf_sz)
-	, _buf(nullptr)
 	, _mode(kStream_Read)
-	, _pos(0) {
+	, _pos(0)
+	, _buf(nullptr) {
 }
 
 MemoryStream::MemoryStream(uint8_t *buf, size_t buf_sz, StreamWorkMode mode, DataEndianess stream_endianess)
 	: DataStream(stream_endianess)
-	, _buf(buf)
+	, _cbuf(nullptr)
 	, _buf_sz(buf_sz)
 	, _len(0)
-	, _cbuf(nullptr)
 	, _mode(mode)
-	, _pos(0) {
+	, _pos(0)
+	, _buf(nullptr) {
+	if (mode == kStream_Read) {
+		_cbuf = buf;
+		_len = buf_sz;
+	} else {
+		_buf = buf;
+	}
 }
 
 void MemoryStream::Close() {
@@ -83,7 +89,7 @@ bool MemoryStream::CanWrite() const {
 }
 
 bool MemoryStream::CanSeek() const {
-	return CanRead(); // TODO: support seeking in writable stream?
+	return true;
 }
 
 size_t MemoryStream::Read(void *buffer, size_t size) {
@@ -123,20 +129,25 @@ bool MemoryStream::Seek(soff_t offset, StreamSeek origin) {
 }
 
 size_t MemoryStream::Write(const void *buffer, size_t size) {
-	if (_pos >= _buf_sz) {
+	if (!_buf || (_pos >= _buf_sz)) {
 		return 0;
 	}
 	size = MIN(size, _buf_sz - _pos);
 	memcpy(_buf + _pos, buffer, size);
 	_pos += size;
-	_len += size;
+	// will increase len if writing after eos, otherwise = overwrite at pos
+	_len = MAX(_len, _pos);
 	return size;
 }
 
 int32_t MemoryStream::WriteByte(uint8_t val) {
-	if (_pos >= _buf_sz) { return -1; }
+	if (!_buf || (_pos >= _buf_sz)) {
+		return -1;
+	}
 	*(_buf + _pos) = val;
-	_pos++; _len++;
+	_pos++;
+	// will increase len if writing after eos, otherwise = overwrite at pos
+	_len = MAX(_len, _pos);
 	return val;
 }
 
@@ -147,7 +158,7 @@ VectorStream::VectorStream(const std::vector<uint8_t> &cbuf, DataEndianess strea
 }
 
 VectorStream::VectorStream(std::vector<uint8_t> &buf, StreamWorkMode mode, DataEndianess stream_endianess)
-	: MemoryStream((mode == kStream_Read) ? &buf.front() : nullptr, buf.size(), mode, stream_endianess)
+	: MemoryStream(((mode == kStream_Read) && (buf.size() > 0)) ? &buf.front() : nullptr, buf.size(), mode, stream_endianess)
 	, _vec(&buf) {
 }
 
@@ -156,17 +167,32 @@ void VectorStream::Close() {
 	MemoryStream::Close();
 }
 
+bool VectorStream::CanRead() const {
+	return _mode == kStream_Read;
+}
+
+bool VectorStream::CanWrite() const {
+	return _mode == kStream_Write;
+}
+
 size_t VectorStream::Write(const void *buffer, size_t size) {
-	_vec->resize(_vec->size() + size);
+	if (_pos + size > _len) {
+		_vec->resize(_pos + size);
+		_len = _pos + size;
+	}
 	memcpy(_vec->data() + _pos, buffer, size);
 	_pos += size;
-	_len += size;
 	return size;
 }
 
 int32_t VectorStream::WriteByte(uint8_t val) {
-	_vec->push_back(val);
-	_pos++; _len++;
+	if (_pos == _len) {
+		_vec->push_back(val);
+		_len++;
+	} else {
+		(*_vec)[_pos] = val;
+	}
+	_pos++;
 	return val;
 }
 
diff --git a/engines/ags/shared/util/memory_stream.h b/engines/ags/shared/util/memory_stream.h
index 5cba412d206..d3394412b8d 100644
--- a/engines/ags/shared/util/memory_stream.h
+++ b/engines/ags/shared/util/memory_stream.h
@@ -78,14 +78,14 @@ public:
 	bool    Seek(soff_t offset, StreamSeek origin) override;
 
 protected:
-	const uint8_t *_cbuf;
-	size_t                   _buf_sz; // hard buffer limit
-	size_t                   _len; // calculated length of stream
+	const uint8_t           *_cbuf = nullptr; // readonly buffer ptr
+	size_t                   _buf_sz = 0u; // hard buffer limit
+	size_t                   _len = 0u; // calculated length of stream
 	const StreamWorkMode     _mode;
-	size_t                   _pos; // current stream pos
+	size_t                   _pos = 0u; // current stream pos
 
 private:
-	uint8_t *_buf;
+	uint8_t                 *_buf = nullptr; // writeable buffer ptr
 };
 
 
@@ -101,11 +101,14 @@ public:
 
 	void    Close() override;
 
+	bool    CanRead() const override;
+	bool    CanWrite() const override;
+
 	size_t  Write(const void *buffer, size_t size) override;
 	int32_t WriteByte(uint8_t b) override;
 
 private:
-	std::vector<uint8_t> *_vec; // writeable vector (may be null)
+	std::vector<uint8_t> *_vec = nullptr; // writeable vector (may be null)
 };
 
 } // namespace Shared


Commit: e26adb919e9703f89a488b2175efa565af0aab4d
    https://github.com/scummvm/scummvm/commit/e26adb919e9703f89a488b2175efa565af0aab4d
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2022-06-13T00:30:21+01:00

Commit Message:
AGS: Moved global DlgOpt into show_dialog_options() function

>From upstream 516231d87a582f32221e4a3f86ce9d437c37f642

Changed paths:
    engines/ags/engine/ac/dialog.cpp


diff --git a/engines/ags/engine/ac/dialog.cpp b/engines/ags/engine/ac/dialog.cpp
index 6719c366edb..7ad6415bc05 100644
--- a/engines/ags/engine/ac/dialog.cpp
+++ b/engines/ags/engine/ac/dialog.cpp
@@ -1019,19 +1019,18 @@ void DialogOptions::Close() {
 	delete tempScrn;
 }
 
-DialogOptions DlgOpt;
-
 int show_dialog_options(int _dlgnum, int sayChosenOption, bool _runGameLoopsInBackground) {
-	DlgOpt.Prepare(_dlgnum, _runGameLoopsInBackground);
-	DlgOpt.Show();
-	DlgOpt.Close();
+	DialogOptions dlgopt;
+	dlgopt.Prepare(_dlgnum, _runGameLoopsInBackground);
+	dlgopt.Show();
+	dlgopt.Close();
 
-	int dialog_choice = DlgOpt.chose;
+	int dialog_choice = dlgopt.chose;
 	if (dialog_choice >= 0) { // NOTE: this condition also excludes CHOSE_TEXTPARSER
 		assert(dialog_choice >= 0 && dialog_choice < MAXTOPICOPTIONS);
-		DialogTopic *dialog_topic = DlgOpt.dtop;
+		DialogTopic *dialog_topic = dlgopt.dtop;
 		int32_t &option_flags = dialog_topic->optionflags[dialog_choice];
-		const char *option_name = DlgOpt.dtop->optionnames[dialog_choice];
+		const char *option_name = dlgopt.dtop->optionnames[dialog_choice];
 
 		option_flags |= DFLG_HASBEENCHOSEN;
 		bool sayTheOption = false;


Commit: 02085d54819aaae3c2f9a7f1148b1814d162b5c6
    https://github.com/scummvm/scummvm/commit/02085d54819aaae3c2f9a7f1148b1814d162b5c6
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2022-06-13T00:30:21+01:00

Commit Message:
AGS: Refactored do_conversation()

Split do_conversation into multiple functions, simplified the running
the dialog topics chain code and removed code duplication as possible.
+ Removed the limit of nested dialogs.
+ This also fixes "goto-previous" not working from the first sub-dialog.

>From upstream 7bb167f5f6ee6a1976b829a1d67423d69346dc6e

Changed paths:
    engines/ags/engine/ac/dialog.cpp


diff --git a/engines/ags/engine/ac/dialog.cpp b/engines/ags/engine/ac/dialog.cpp
index 7ad6415bc05..1981f2d8c2c 100644
--- a/engines/ags/engine/ac/dialog.cpp
+++ b/engines/ags/engine/ac/dialog.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/stack.h"
 #include "ags/engine/ac/dialog.h"
 #include "ags/shared/ac/common.h"
 #include "ags/engine/ac/character.h"
@@ -390,7 +391,6 @@ bool get_custom_dialog_options_dimensions(int dlgnum) {
 	return false;
 }
 
-#define MAX_TOPIC_HISTORY 50
 #define DLG_OPTION_PARSER 99
 
 struct DialogOptions {
@@ -1047,69 +1047,65 @@ int show_dialog_options(int _dlgnum, int sayChosenOption, bool _runGameLoopsInBa
 	return dialog_choice;
 }
 
-void do_conversation(int dlgnum) {
-	EndSkippingUntilCharStops();
-
-	// AGS 2.x always makes the mouse cursor visible when displaying a dialog.
-	if (_G(loaded_game_file_version) <= kGameVersion_272)
-		_GP(play).mouse_cursor_hidden = 0;
-
-	int dlgnum_was = dlgnum;
-	int previousTopics[MAX_TOPIC_HISTORY];
-	int numPrevTopics = 0;
-	DialogTopic *dtop = &_G(dialog)[dlgnum];
+// Dialog execution state
+struct DialogExec {
+	int DlgNum = -1;
+	int DlgWas = -1;
+	// CHECKME: this may be unnecessary, investigate later
+	bool IsFirstEntry = true;
+	// nested dialogs "stack"
+	Common::Stack<int> TopicHist;
+
+	DialogExec(int start_dlgnum) : DlgNum(start_dlgnum) {}
+	int HandleDialogResult(int res);
+	void Run();
+};
 
-	// run the startup script
-	int tocar = run_dialog_script(dlgnum, dtop->startupentrypoint, 0);
-	if ((tocar == RUN_DIALOG_STOP_DIALOG) ||
-	        (tocar == RUN_DIALOG_GOTO_PREVIOUS)) {
-		// 'stop' or 'goto-previous' from first startup script
-		remove_screen_overlay(OVER_COMPLETE);
-		_GP(play).in_conversation--;
-		return;
-	} else if (tocar >= 0)
-		dlgnum = tocar;
-
-	while (dlgnum >= 0) {
-		if (dlgnum >= _GP(game).numdialog)
-			quit("!RunDialog: invalid dialog number specified");
-
-		dtop = &_G(dialog)[dlgnum];
-
-		if (dlgnum != dlgnum_was) {
-			// dialog topic changed, so play the startup
-			// script for the new topic
-			tocar = run_dialog_script(dlgnum, dtop->startupentrypoint, 0);
-			dlgnum_was = dlgnum;
-			if (tocar == RUN_DIALOG_GOTO_PREVIOUS) {
-				if (numPrevTopics < 1) {
-					// goto-previous on first topic -- end dialog
-					tocar = RUN_DIALOG_STOP_DIALOG;
-				} else {
-					tocar = previousTopics[numPrevTopics - 1];
-					numPrevTopics--;
-				}
-			}
-			if (tocar == RUN_DIALOG_STOP_DIALOG)
-				break;
-			else if (tocar >= 0) {
-				// save the old topic number in the history
-				if (numPrevTopics < MAX_TOPIC_HISTORY) {
-					previousTopics[numPrevTopics] = dlgnum;
-					numPrevTopics++;
-				}
-				dlgnum = tocar;
-				continue;
-			}
+int DialogExec::HandleDialogResult(int res) {
+	// Handle goto-previous, see if there's any previous dialog in history
+	if (res == RUN_DIALOG_GOTO_PREVIOUS) {
+		if (TopicHist.size() == 0)
+			return RUN_DIALOG_STOP_DIALOG;
+		res = TopicHist.top();
+		TopicHist.pop();
+	}
+	// Continue to the next dialog
+	if (res >= 0) {
+		// save the old topic number in the history, and switch to the new one
+		TopicHist.push(DlgNum);
+		DlgNum = res;
+		return DlgNum;
+	}
+	return res;
+ }
+
+void DialogExec::Run() {
+	while (DlgNum >= 0) {
+		if (DlgNum < 0 || DlgNum >= _GP(game).numdialog)
+			quitprintf("!RunDialog: invalid dialog number specified: %d", DlgNum);
+
+		// current dialog object
+		DialogTopic *dtop = &_G(dialog)[DlgNum];
+		int res = 0; // dialog execution result
+		// If a new dialog topic: run dialog entry point
+		if (DlgNum != DlgWas) {
+			res = run_dialog_script(DlgNum, dtop->startupentrypoint, 0);
+			DlgWas = DlgNum;
+
+			// Handle the dialog entry's result
+			res = HandleDialogResult(res);
+			if (res == RUN_DIALOG_STOP_DIALOG)
+				return; // stop the dialog
+			IsFirstEntry = false;
+			if (res != RUN_DIALOG_STAY)
+				continue; // skip to the next dialog
 		}
 
-		int chose = show_dialog_options(dlgnum, SAYCHOSEN_USEFLAG, (_GP(game).options[OPT_RUNGAMEDLGOPTS] != 0));
-
+		// Show current dialog's options
+		int chose = show_dialog_options(DlgNum, SAYCHOSEN_USEFLAG, (_GP(game).options[OPT_RUNGAMEDLGOPTS] != 0));
 		if (chose == CHOSE_TEXTPARSER) {
 			_G(said_speech_line) = 0;
-
-			tocar = run_dialog_request(dlgnum);
-
+			res = run_dialog_request(DlgNum);
 			if (_G(said_speech_line) > 0) {
 				// fix the problem with the close-up face remaining on screen
 				DisableInterface();
@@ -1118,28 +1114,34 @@ void do_conversation(int dlgnum) {
 				set_mouse_cursor(CURS_ARROW);
 			}
 		} else if (chose >= 0) {
-			tocar = run_dialog_script(dlgnum, dtop->entrypoints[chose], chose + 1);
+			// chose some option - run its script
+			res = run_dialog_script(DlgNum, dtop->entrypoints[chose], chose + 1);
 		} else {
-			tocar = RUN_DIALOG_STOP_DIALOG;
+			return; // no option chosen? - stop the dialog
 		}
 
-		if (tocar == RUN_DIALOG_GOTO_PREVIOUS) {
-			if (numPrevTopics < 1) {
-				tocar = RUN_DIALOG_STOP_DIALOG;
-			} else {
-				tocar = previousTopics[numPrevTopics - 1];
-				numPrevTopics--;
-			}
-		}
-		if (tocar == RUN_DIALOG_STOP_DIALOG) break;
-		else if (tocar >= 0) {
-			// save the old topic number in the history
-			if (numPrevTopics < MAX_TOPIC_HISTORY) {
-				previousTopics[numPrevTopics] = dlgnum;
-				numPrevTopics++;
-			}
-			dlgnum = tocar;
-		}
+		// Handle the dialog option's result
+		res = HandleDialogResult(res);
+		if (res == RUN_DIALOG_STOP_DIALOG)
+			return; // stop the dialog
+		// continue to the next dialog or show same dialog's options again
+	}
+}
+
+void do_conversation(int dlgnum) {
+	EndSkippingUntilCharStops();
+
+	// AGS 2.x always makes the mouse cursor visible when displaying a dialog.
+	if (_G(loaded_game_file_version) <= kGameVersion_272)
+		_GP(play).mouse_cursor_hidden = 0;
+
+	DialogExec dlgexec(dlgnum);
+	dlgexec.Run();
+	// CHECKME: find out if this is safe to do always, regardless of number of iterations
+	if (dlgexec.IsFirstEntry) {
+		// bail out from first startup script
+		remove_screen_overlay(OVER_COMPLETE);
+		_GP(play).in_conversation--;
 	}
 }
 




More information about the Scummvm-git-logs mailing list