diff --git a/game/game/gamesession.cpp b/game/game/gamesession.cpp index a3fc1676a..6f5c5492e 100644 --- a/game/game/gamesession.cpp +++ b/game/game/gamesession.cpp @@ -196,8 +196,8 @@ void GameSession::save(Serialize &fout, std::string_view name, const Pixmap& scr fout.write(i.name); } - fout.setEntry("preview.png"); - fout.write(screen); + fout.setEntry("preview.jpg"); + fout.write(std::tie(screen,"jpg")); fout.setEntry("game/session"); fout.write(ticks,wrldTime,wrldTimePart,wrld->name()); diff --git a/game/game/serialize.cpp b/game/game/serialize.cpp index b1e6b54e2..c3422b3a0 100644 --- a/game/game/serialize.cpp +++ b/game/game/serialize.cpp @@ -273,10 +273,14 @@ void Serialize::implRead(SaveGameHeader& p) { } void Serialize::implWrite(const Tempest::Pixmap& p) { + implWrite(p, "png"); + } + +void Serialize::implWrite(const Tempest::Pixmap& p, const char* ext) { std::vector tmp; tmp.reserve(4*1024*1024); Tempest::MemWriter w{tmp}; - p.save(w); + p.save(w, ext); writeBytes(tmp.data(),tmp.size()); } diff --git a/game/game/serialize.h b/game/game/serialize.h index 8d2b5a29d..0ec3d2af1 100644 --- a/game/game/serialize.h +++ b/game/game/serialize.h @@ -250,6 +250,9 @@ class Serialize { void implRead (SaveGameHeader& p); void implWrite(const Tempest::Pixmap& p); + void implWrite(const Tempest::Pixmap& p, const char* ext); + template + void implWrite(std::tuple p) { implWrite(std::get<0>(p), std::get<1>(p)); } void implRead (Tempest::Pixmap& p); void implWrite(const zenkit::INpc& h); diff --git a/game/graphics/shaders.cpp b/game/graphics/shaders.cpp index 2c72a458b..f4c0daec3 100644 --- a/game/graphics/shaders.cpp +++ b/game/graphics/shaders.cpp @@ -42,7 +42,8 @@ Shaders& Shaders::inst() { } void Shaders::compileKeyShaders() { - bink = postEffect("bink"); + bink = postEffect("bink"); + downscale = postEffect("downscale"); } void Shaders::compileShaders() { @@ -50,13 +51,13 @@ void Shaders::compileShaders() { const bool meshlets = Gothic::options().doMeshShading; - copyBuf = computeShader("copy.comp.sprv"); - copyImg = computeShader("copy_img.comp.sprv"); - copy = postEffect("copy"); + copyBuf = computeShader("copy.comp.sprv"); + copyImg = computeShader("copy_img.comp.sprv"); + copy = postEffect("copy"); - patch = computeShader("patch.comp.sprv"); + patch = computeShader("patch.comp.sprv"); - stash = postEffect("stash"); + stash = postEffect("stash"); clusterInit = computeShader("cluster_init.comp.sprv"); clusterPatch = computeShader("cluster_patch.comp.sprv"); diff --git a/game/graphics/shaders.h b/game/graphics/shaders.h index 9f113e5c2..fe9c77ba6 100644 --- a/game/graphics/shaders.h +++ b/game/graphics/shaders.h @@ -35,7 +35,7 @@ class Shaders { Tempest::ComputePipeline copyBuf; Tempest::ComputePipeline copyImg; Tempest::ComputePipeline patch; - Tempest::RenderPipeline copy; + Tempest::RenderPipeline copy, downscale; Tempest::RenderPipeline stash; Tempest::RenderPipeline bink; diff --git a/game/mainwindow.cpp b/game/mainwindow.cpp index 924521453..ecc3bccb6 100644 --- a/game/mainwindow.cpp +++ b/game/mainwindow.cpp @@ -1063,14 +1063,39 @@ void MainWindow::loadGame(std::string_view slot) { } void MainWindow::saveGame(std::string_view slot, std::string_view name) { - auto tex = renderer.screenshoot(cmdId); - auto pm = device.readPixels(textureCast(tex)); - if(dialogs.isActive()) return; if(auto w = Gothic::inst().world(); w!=nullptr && w->currentCs()!=nullptr) return; + auto tex = renderer.screenshoot(cmdId); + auto lres = Attachment(); + + static int32_t kThumbW = 800; + const int32_t kThumbH = tex.w()>0 ? int32_t((tex.h() * kThumbW) / tex.w()) : 0; + if(kThumbW>0 && kThumbH>0 && kThumbW(thumb)); + Gothic::inst().startSave(std::move(textureCast(tex)),[slot=std::string(slot),name=std::string(name),pm](std::unique_ptr&& game){ if(!game) return std::move(game); diff --git a/game/ui/gamemenu.cpp b/game/ui/gamemenu.cpp index 70f0e142a..61798b47b 100644 --- a/game/ui/gamemenu.cpp +++ b/game/ui/gamemenu.cpp @@ -1012,6 +1012,8 @@ void GameMenu::updateSavTitle(GameMenu::Item& sel) { reader.read(sel.savPriview); // legacy else if(reader.setEntry("preview.png")) reader.read(sel.savPriview); + else if(reader.setEntry("preview.jpg")) + reader.read(sel.savPriview); } catch(std::bad_alloc&) { return; diff --git a/shader/CMakeLists.txt b/shader/CMakeLists.txt index 4eda6621a..f58d076d2 100644 --- a/shader/CMakeLists.txt +++ b/shader/CMakeLists.txt @@ -197,6 +197,7 @@ add_shader(item.frag inventory/item.frag) add_shader(stash.frag stash.frag) add_shader(bink.frag bink/bink.frag) +add_shader(downscale.frag downscale.frag) add_shader(direct_light.frag lighting/direct_light.frag) add_shader(direct_light_sh.frag lighting/direct_light.frag -DSHADOW_MAP) diff --git a/shader/downscale.frag b/shader/downscale.frag new file mode 100644 index 000000000..9f63d3964 --- /dev/null +++ b/shader/downscale.frag @@ -0,0 +1,22 @@ +#version 450 + +layout(binding = 0) uniform sampler2D src; +layout(location = 0) out vec4 outColor; + +layout(push_constant, std140) uniform Push { + ivec2 dstSize; + }; + +void main() { + ivec2 srcSize = textureSize(src, 0); + + ivec2 tl = (ivec2(gl_FragCoord.xy+ivec2(0))*srcSize)/dstSize; + ivec2 br = (ivec2(gl_FragCoord.xy+ivec2(1))*srcSize)/dstSize; + + vec4 color = vec4(0); + for(int i=tl.x; i