Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Canvas is the wrong size for the graphics when writing directly to pixels and using a high DPI and using SDL_WINDOW_ALLOW_HIGHDPI #22944

Open
inhahe opened this issue Nov 15, 2024 · 9 comments

Comments

@inhahe
Copy link

inhahe commented Nov 15, 2024

Please include the following in your bug report:

Version of emscripten/emsdk:
3.1.71

Full link command and output with -v appended:

 "/home/inhahe/emsdk/upstream/bin/clang++" -target wasm32-unknown-emscripten -fignore-exceptions -mno-bulk-memory -mno-nontrapping-fptoint -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr --sysroot=/home/inhahe/emsdk/upstream/emscripten/cache/sysroot -DEMSCRIPTEN -isystem /home/inhahe/emsdk/upstream/emscripten/cache/sysroot/include/SDL2 -Xclang -iwithsysroot/include/compat -v "/mnt/d/visual studio projects/scribbles3/sdl2bug.cpp" -c -o /tmp/emscripten_temp_cpdzmkie/sdl2bug_0.o
clang version 20.0.0git (https:/github.com/llvm/llvm-project d6344c1cd0d099f8d99ee320f33fc9254dbe8288)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: /home/inhahe/emsdk/upstream/bin
 (in-process)
 "/home/inhahe/emsdk/upstream/bin/clang-20" -cc1 -triple wasm32-unknown-emscripten -emit-obj -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name sdl2bug.cpp -mrelocation-model static -mframe-pointer=none -ffp-contract=on -fno-rounding-math -mconstructor-aliases -target-cpu generic -target-feature -bulk-memory -target-feature -nontrapping-fptoint -fvisibility=hidden -debugger-tuning=gdb -fdebug-compilation-dir=/home/inhahe -v -fcoverage-compilation-dir=/home/inhahe -resource-dir /home/inhahe/emsdk/upstream/lib/clang/20 -isystem /home/inhahe/emsdk/upstream/emscripten/cache/sysroot/include/SDL2 -D EMSCRIPTEN -isysroot /home/inhahe/emsdk/upstream/emscripten/cache/sysroot -internal-isystem /home/inhahe/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten/c++/v1 -internal-isystem /home/inhahe/emsdk/upstream/emscripten/cache/sysroot/include/c++/v1 -internal-isystem /home/inhahe/emsdk/upstream/lib/clang/20/include -internal-isystem /home/inhahe/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten -internal-isystem /home/inhahe/emsdk/upstream/emscripten/cache/sysroot/include -fdeprecated-macro -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcxx-exceptions -fignore-exceptions -fexceptions -fcolor-diagnostics -iwithsysroot/include/compat -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr -o /tmp/emscripten_temp_cpdzmkie/sdl2bug_0.o -x c++ "/mnt/d/visual studio projects/scribbles3/sdl2bug.cpp"
clang -cc1 version 20.0.0git based upon LLVM 20.0.0git default target x86_64-unknown-linux-gnu
ignoring nonexistent directory "/home/inhahe/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten/c++/v1"
ignoring nonexistent directory "/home/inhahe/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten"
#include "..." search starts here:
#include <...> search starts here:
 /home/inhahe/emsdk/upstream/emscripten/cache/sysroot/include/SDL2
 /home/inhahe/emsdk/upstream/emscripten/cache/sysroot/include/compat
 /home/inhahe/emsdk/upstream/emscripten/cache/sysroot/include/c++/v1
 /home/inhahe/emsdk/upstream/lib/clang/20/include
 /home/inhahe/emsdk/upstream/emscripten/cache/sysroot/include
End of search list.
 /home/inhahe/emsdk/upstream/bin/clang --version
 /home/inhahe/emsdk/upstream/bin/wasm-ld -o sdl2bug.wasm /tmp/emscripten_temp_cpdzmkie/sdl2bug_0.o -L/home/inhahe/emsdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten /home/inhahe/emsdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten/libSDL2.a -lGL-getprocaddr -lal -lhtml5 -lstubs-debug -lnoexit -lc-debug -ldlmalloc -lcompiler_rt -lc++-noexcept -lc++abi-debug-noexcept -lsockets -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr /tmp/tmp0gqt38m2libemscripten_js_symbols.so --strip-debug --export=emscripten_stack_get_end --export=emscripten_stack_get_free --export=emscripten_stack_get_base --export=emscripten_stack_get_current --export=emscripten_stack_init --export=_emscripten_stack_alloc --export=__get_temp_ret --export=__set_temp_ret --export=__wasm_call_ctors --export=_emscripten_stack_restore --export-if-defined=__start_em_asm --export-if-defined=__stop_em_asm --export-if-defined=__start_em_lib_deps --export-if-defined=__stop_em_lib_deps --export-if-defined=__start_em_js --export-if-defined=__stop_em_js --export-if-defined=main --export-if-defined=__main_argc_argv --export-if-defined=fflush --export-table -z stack-size=65536 --max-memory=2147483648 --initial-heap=16777216 --no-entry --stack-first --table-base=1
 /home/inhahe/emsdk/upstream/bin/llvm-objcopy sdl2bug.wasm sdl2bug.wasm --remove-section=.debug* --remove-section=producers
 /home/inhahe/emsdk/upstream/bin/wasm-emscripten-finalize --dyncalls-i64 --pass-arg=legalize-js-interface-exported-helpers sdl2bug.wasm -o sdl2bug.wasm --detect-features
 /home/inhahe/emsdk/node/20.18.0_64bit/bin/node /home/inhahe/emsdk/upstream/emscripten/src/compiler.mjs /tmp/tmpapet3dfw.json
 /home/inhahe/emsdk/node/20.18.0_64bit/bin/node /home/inhahe/emsdk/upstream/emscripten/tools/preprocessor.mjs /tmp/emscripten_temp_cpdzmkie/settings.js shell.html

I've made a minimal example that exhibits this behavior.
Ideally, the whole canvas should turn red.
But on a high-DPI screen, the canvas is about 1.5x the size of the graphics (the red square).
Changing the canvas size is CSS only shrinks the whole thing, with the red square still taking only a portion of the canvas.
The parts of the canvas not used show up in black.
Honestly, I'm not sure how this stuff works well enough to know whether this is an Emscripten bug or an SDL bug. But... I have a version of the same program for the desktop instead of the web, and it doesn't have this problem.

#include <emscripten.h>
#include <SDL.h>

int width = 1000;
int height = 1000;

struct Context
{
  SDL_Renderer* renderer = nullptr;
  SDL_Window* window = nullptr;
  SDL_Surface* surface = nullptr;
  SDL_PixelFormat* pixel_format_surface = nullptr;
};

void mainloop(void* arg)
{ 
  Context* context = static_cast<Context*>(arg);
  SDL_RenderClear(context->renderer);
  uint8_t* pixels = (uint8_t*)(context->surface->pixels);
  int pitch = context->surface->pitch;
  int color = SDL_MapRGBA(context->pixel_format_surface, 255, 0, 0, 255);
  if (SDL_MUSTLOCK(context->surface)) SDL_LockSurface(context->surface);
  uint8_t* sp_pixels = nullptr;
  for (int y = 0; y < height; y++)
  {
    sp_pixels = pixels + y * pitch;
    for (int x = 0; x < width; x++)
    {
      *(uint32_t*)sp_pixels = color;
      sp_pixels += 4;
    }
  }
  if (SDL_MUSTLOCK(context->surface)) SDL_UnlockSurface(context->surface);
  SDL_UpdateWindowSurface(context->window);
}

int main(int argc, char* argv[])
{
  Context context;
  SDL_Init(SDL_INIT_VIDEO);
  context.window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_ALLOW_HIGHDPI);
  context.surface = SDL_GetWindowSurface(context.window);
  context.pixel_format_surface = context.surface->format;
  emscripten_set_main_loop_arg(mainloop, &context, 0, 1);
}

Here's a screenshot of how it looks on my display:
image

@sbc100
Copy link
Collaborator

sbc100 commented Nov 15, 2024

So this bug only occurs with SDL_WINDOW_ALLOW_HIGHDPI?

I would assume this is an SDL bug. @Daft-Freak WDYT?

@sbc100
Copy link
Collaborator

sbc100 commented Nov 15, 2024

Also, is this a regression? (i.e. did it used to work in the past?)

@Daft-Freak
Copy link
Collaborator

I think this is this old issue: emscripten-ports/SDL2#109. Specifically, the combination of framebuffer rendering and HiDPI, which when I looked at it back then was handled in other ports by... basically not doing HiDPI.

@inhahe
Copy link
Author

inhahe commented Nov 16, 2024

@sbc100 I don't know of it ever working right in the past, but I haven't been using Emscripten for very long.

@Daft-Freak I really want HighDPI to work, I dislike the automatic upscaling, at least for the project I'm doing now, which is my only Emscripten project. I would like to see the HighDPI flag work across the board (and correctly), not just with this particular rendering method.. :P (I noticed that any other rendering method just ignores AllowHighDPI..)

@inhahe
Copy link
Author

inhahe commented Nov 16, 2024

I don't like the two workarounds that are suggested in emscripten-ports/SDL2#109,

"As a workaround, you can use SDL_SetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION, "1"); before you call SDL_Init to force going through the renderer.

EDIT: Hmm, since the framebuffer seems to always be non-HiDPI you could just disable HiDPI and use CSS image-rendering on the canvas to control the scaling..."

The first one would just have the effect of ignoring allowhighdpi, if I understand correctly, since in my experience all the other rendering methods ignore allowhighdpi.

And to the second one, I want a one-to-one pixel correspondence between the rendering and the monitor, and I don't know how to find out exactly how much I'd have to scale it to get that ratio, let alone in a 100% reliable way...

@inhahe
Copy link
Author

inhahe commented Nov 16, 2024

Sorry to ask in the issue box, but I've been looking and asking everywhere and I can't seem to find answers: how can I write to the pixel data of the canvas without using SDL?

@Daft-Freak
Copy link
Collaborator

Looking at this more closely, it seems that the framebuffer is correctly sized now and switching width/height for context->surface->w/->h in the loop fills the entire canvas.

2024-11-16T11:36:29,819973586+00:00
(That ended up at the correct scale (1.5x) but goes through a 2x scale in the middle because devicePixelRatio is 2 for some reason. That seems like a browser thing outside of SDL's control though...)

@inhahe
Copy link
Author

inhahe commented Nov 16, 2024

@Daft-Freak Oh, that helps, but it's still kind of awkward because i wanted a 1000x1000 canvas, not an approximately 1500x1500 canvas. and if i just create my window 66% as large then it will be too small on monitors that don't have a high dpi.. (it'll be 666x666)
But this is a great find, I should have realized it.

@inhahe
Copy link
Author

inhahe commented Nov 16, 2024

Could you guys update GLFW support to at least version 3.3? I tried switching the program over to GLFW to have a 1-to-1 pixel correspondence, and it upscales the window too! But GPT-4 told me I could use glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE); to make it not do that, but that it's only available in GLFW 3.3 and later. I made my program print out the version of GLFW it's using, and it says 3.2.1. I tried compiling GLFW 3.4 with emcmake and emmake myself, but I got errors. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants