Files
spice2x-r3d/src/spice2x/external/libjpeg-turbo/jmemnobs.c
T
bicarusandGitHub 8b2f38307b graphics: rewrite screenshot and api capture image processing (#870)
## Link to GitHub Issue or related Pull Request, if one exists
#0

## Description of change
Significantly speeds up API screen capture and D3D9 screenshots saving.
Two reasons for doing this:

1. We now have a 4K game (GITADORA) and existing capture code was taking
multiple seconds.
2. Renewed user interest on streaming as we have a couple more companion
apps in active development.

**API screen capture (streaming), 1280x720:** 14.3ms -> 6.3ms per frame.
Back buffer copies go to pooled `D3DPOOL_SYSTEMMEM` surfaces via
`GetRenderTargetData` instead of allocating a lockable render target
every frame, and TooJpeg is replaced with libjpeg-turbo (encode 9.8ms ->
3.0ms). MSAA remains unsupported

**Screenshots for GITADORA arena model, across 4 screens with one of
them 4K**: 4068ms -> 124ms. `D3DXSaveSurfaceToFileA` is replaced with
fpng (encode 4043ms -> 76ms) and the screens encode in parallel.
Dropping D3DX also removes the `d3dx9_43.dll` ... `d3dx9_24.dll` probing
loop, so screenshots no longer fail outright on machines with no D3DX9
runtime installed.

Screenshot surfaces are read on the present thread, so no D3D call
reaches another thread for screenshots. This fixes a hang in DDR X2
introduced earlier in the branch: its device has no internal locking,
and reading the surface on a pool thread while the present thread sat
inside `GetRenderTargetData` left the game's own render thread
deadlocked.


## Testing

- **GITADORA** (arena model, D3D9Ex, 4K main plus three subscreens,
windowed) with
`-screenshotsub`: three sets of four screenshots, images verified
correct. Completion
order differs between sets, so the screens really are encoding in
parallel.
- **LovePlus** (KLP, plain D3D9, 768x1360): covers the inline path used
by games whose
  image processing must not leave the present thread. 
- **API screen capture** through a companion app: live video correct
throughout.
- **Print Screen** bound as the screenshot key: the clipboard copy
succeeded on every shot.
- Quitting the game after capturing leaves no `IDirect3DDevice9`
reference count warning,
  so the pooled readback surfaces are released along with the device.
2026-08-18 00:22:45 -07:00

111 lines
2.6 KiB
C

/*
* jmemnobs.c
*
* This file was part of the Independent JPEG Group's software:
* Copyright (C) 1992-1996, Thomas G. Lane.
* libjpeg-turbo Modifications:
* Copyright (C) 2017-2018, 2024, D. R. Commander.
* For conditions of distribution and use, see the accompanying README.ijg
* file.
*
* This file provides a really simple implementation of the system-
* dependent portion of the JPEG memory manager. This implementation
* assumes that no backing-store files are needed: all required space
* can be obtained from malloc().
* This is very portable in the sense that it'll compile on almost anything,
* but you'd better have lots of main memory (or virtual memory) if you want
* to process big images.
*/
#define JPEG_INTERNALS
#include "jinclude.h"
#include "jpeglib.h"
#include "jmemsys.h" /* import the system-dependent declarations */
/*
* Memory allocation and freeing are controlled by the regular library
* routines malloc() and free().
*/
GLOBAL(void *)
jpeg_get_small(j_common_ptr cinfo, size_t sizeofobject)
{
return (void *)MALLOC(sizeofobject);
}
GLOBAL(void)
jpeg_free_small(j_common_ptr cinfo, void *object, size_t sizeofobject)
{
free(object);
}
/*
* "Large" objects are treated the same as "small" ones.
*/
GLOBAL(void *)
jpeg_get_large(j_common_ptr cinfo, size_t sizeofobject)
{
return (void *)MALLOC(sizeofobject);
}
GLOBAL(void)
jpeg_free_large(j_common_ptr cinfo, void *object, size_t sizeofobject)
{
free(object);
}
/*
* This routine computes the total memory space available for allocation.
*/
GLOBAL(size_t)
jpeg_mem_available(j_common_ptr cinfo, size_t min_bytes_needed,
size_t max_bytes_needed, size_t already_allocated)
{
if (cinfo->mem->max_memory_to_use) {
if ((size_t)cinfo->mem->max_memory_to_use > already_allocated)
return cinfo->mem->max_memory_to_use - already_allocated;
else
return 0;
} else {
/* Here we always say, "we got all you want bud!" */
return max_bytes_needed;
}
}
/*
* Backing store (temporary file) management.
* Since jpeg_mem_available always promised the moon,
* this should never be called and we can just error out.
*/
GLOBAL(void)
jpeg_open_backing_store(j_common_ptr cinfo, backing_store_ptr info,
long total_bytes_needed)
{
ERREXIT(cinfo, JERR_NO_BACKING_STORE);
}
/*
* These routines take care of any system-dependent initialization and
* cleanup required. Here, there isn't any.
*/
GLOBAL(long)
jpeg_mem_init(j_common_ptr cinfo)
{
return 0; /* just set max_memory_to_use to 0 */
}
GLOBAL(void)
jpeg_mem_term(j_common_ptr cinfo)
{
/* no work */
}