Skip to content

Commit

Permalink
Merge branch 'main' into release
Browse files Browse the repository at this point in the history
spd789562 committed Aug 23, 2024
2 parents 15ac585 + 0ec1d01 commit 6a37532
Showing 43 changed files with 596 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -57,7 +57,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tagName: app-v__VERSION__ # the action automatically replaces \_\_VERSION\_\_ with the app version.
releaseName: "App v__VERSION__"
releaseName: "v__VERSION__"
releaseBody: "See the assets to download this version and install."
releaseDraft: true
prerelease: false
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
## [0.1.0]
- 首次發布測試版
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,20 @@
# Maple Character Creator
# MapleSalon2
Salon Simulator for Maplestory, preview all of hair cut or eyes color and all mix dye recipe.

Now also able to simulate item dye! and preview all action.

## Screenshot
![](./doc/preview_app.png)

## Download
All download avaiable at [Releases page](./releases).

Installer will only check `Webview` and extract the app exe to desire folder, fell free to move it anywhere.

## Build it manually
Make sure you have environment below.
- Rust 1.70.0 or higher
- Node.js 16 or higher

then
> npm run tauri build
Binary file removed bun.lockb
Binary file not shown.
Binary file added doc/preview_app.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<link rel="icon" type="image/svg+xml" href="/src/assets/logo.svg" />
<link rel="icon" type="image/png" href="/src/assets/icon.png" />
<title>Tauri + Solid + Typescript App</title>
</head>

15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -25,6 +25,7 @@
"@tauri-apps/plugin-store": "^2.0.0-rc.0",
"@tauri-apps/plugin-window-state": "^2.0.0-rc.0",
"@zip.js/zip.js": "^2.7.47",
"anime4k-webgpu": "^1.0.0",
"dom-to-image-more": "^3.3.1",
"lucide-solid": "^0.394.0",
"mingcute_icon": "^2.9.4",
6 changes: 0 additions & 6 deletions public/tauri.svg

This file was deleted.

1 change: 0 additions & 1 deletion public/vite.svg

This file was deleted.

2 changes: 1 addition & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "maplesalon2"
version = "0.0.1"
version = "0.1.0"
description = "MapleSalon2 - A tool for preview hair, face and item dye using MapleStory wz files."
authors = ["Leo"]
edition = "2021"
Binary file modified src-tauri/icons/128x128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src-tauri/icons/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src-tauri/icons/32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src-tauri/icons/Square107x107Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src-tauri/icons/Square142x142Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src-tauri/icons/Square150x150Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src-tauri/icons/Square284x284Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src-tauri/icons/Square30x30Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src-tauri/icons/Square310x310Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src-tauri/icons/Square44x44Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src-tauri/icons/Square71x71Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src-tauri/icons/Square89x89Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src-tauri/icons/StoreLogo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src-tauri/icons/icon.icns
Binary file not shown.
Binary file modified src-tauri/icons/icon.ico
Binary file not shown.
Binary file modified src-tauri/icons/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 11 additions & 3 deletions src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"productName": "MapleSalon2",
"version": "0.0.0",
"identifier": "com.maplecreator.io",
"version": "0.1.0",
"identifier": "com.maplesalon.io",
"build": {
"beforeDevCommand": "npm run dev",
"devUrl": "http://localhost:1420",
@@ -29,6 +29,14 @@
"icons/[email protected]",
"icons/icon.icns",
"icons/icon.ico"
]
],
"windows": {
"nsis": {
"languages": ["English", "TradChinese"]
},
"wix": {
"language": ["en-US", "zh-TW", "zh-CN"]
}
}
}
}
Binary file added src/assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion src/assets/logo.svg

This file was deleted.

13 changes: 11 additions & 2 deletions src/components/CharacterPreview/Character.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { onMount, onCleanup, createEffect, createSignal } from 'solid-js';
import type { ReadableAtom } from 'nanostores';


import { $preferRenderer } from '@/store/renderer';
import type { CharacterData } from '@/store/character/store';
import {
@@ -12,6 +11,10 @@ import {
updateCenter,
updateZoom,
} from '@/store/previewZoom';
import {
resetUpscaleSource,
setUpscaleSource,
} from '@/store/expirement/upscale';
import { usePureStore } from '@/store';

import { Application } from 'pixi.js';
@@ -43,7 +46,7 @@ export const CharacterView = (props: CharacterViewProps) => {
height: 340,
background: 0x000000,
backgroundAlpha: 0,
antialias: true,
// antialias: true,
preference: $preferRenderer.get(),
});
viewport = new ZoomContainer(app, {
@@ -77,6 +80,9 @@ export const CharacterView = (props: CharacterViewProps) => {
container.appendChild(app.canvas);
viewport.addChild(ch);
app.stage.addChild(viewport);
if (props.target === 'preview') {
setUpscaleSource(app.canvas);
}

setIsInit(true);
}
@@ -88,6 +94,9 @@ export const CharacterView = (props: CharacterViewProps) => {
onCleanup(() => {
ch.reset();
app.destroy();
if (props.target === 'preview') {
setUpscaleSource(app.canvas);
}
});

createEffect(() => {
14 changes: 13 additions & 1 deletion src/components/CharacterPreview/CharacterScene.tsx
Original file line number Diff line number Diff line change
@@ -8,13 +8,18 @@ import {
$previewCharacter,
$sceneCustomColorStyle,
} from '@/store/character/selector';
import { $showPreviousCharacter } from '@/store/trigger';
import {
$showPreviousCharacter,
$showUpscaledCharacter,
} from '@/store/trigger';

import LoaderCircle from 'lucide-solid/icons/loader-circle';
import ChevronRightIcon from 'lucide-solid/icons/chevron-right';
import { CharacterView } from './Character';
import { UpscaleCharacter } from './UpscaleCharacter';
import { CharacterSceneSelection } from './CharacterSceneSelection';
import { ShowPreviousSwitch } from './ShowPreviousSwitch';
import { ShowUpscaleSwitch } from './ShowUpscaleSwitch';
import { ZoomControl } from './ZoomControl';

import { PreviewSceneBackground } from '@/const/scene';
@@ -26,6 +31,7 @@ export const CharacterScene = () => {
const scene = useStore($currentScene);
const customColorStyle = useStore($sceneCustomColorStyle);
const isShowComparison = useStore($showPreviousCharacter);
const isShowUpscale = useStore($showUpscaledCharacter);

function handleLoad() {
setIsLoading(true);
@@ -83,8 +89,12 @@ export const CharacterScene = () => {
target="preview"
isLockInteraction={isLockInteraction()}
/>
<Show when={isShowUpscale()}>
<UpscaleCharacter />
</Show>
<TopTool>
<ShowPreviousSwitch />
<ShowUpscaleSwitch />
</TopTool>
<BottomLeftTool>
<ZoomControl />
@@ -144,6 +154,8 @@ const TopTool = styled('div', {
transition: 'opacity 0.2s',
backgroundColor: 'bg.default',
boxShadow: 'md',
display: 'flex',
gap: 2,
_hover: {
opacity: 1,
},
24 changes: 24 additions & 0 deletions src/components/CharacterPreview/ShowUpscaleSwitch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Show } from 'solid-js';
import { useStore } from '@nanostores/solid';

import { $enableExperimentalUpscale } from '@/store/settingDialog';
import { $showUpscaledCharacter } from '@/store/trigger';

import { Switch, type ChangeDetails } from '@/components/ui/switch';

export const ShowUpscaleSwitch = () => {
const isEnable = useStore($enableExperimentalUpscale);
const isShow = useStore($showUpscaledCharacter);

function handleChange(details: ChangeDetails) {
$showUpscaledCharacter.set(details.checked);
}

return (
<Show when={isEnable()}>
<Switch checked={isShow()} onCheckedChange={handleChange}>
顯示高清化
</Switch>
</Show>
);
};
91 changes: 91 additions & 0 deletions src/components/CharacterPreview/UpscaleCharacter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { onMount, onCleanup, createEffect, createSignal } from 'solid-js';

import { $upscaleSource } from '@/store/expirement/upscale';
import { usePureStore } from '@/store';

import {
PipelineType,
createGpuDevice,
createRendererWithPipelines,
type GpuResource,
type Anime4KRenderer,
type PipelineOption,
} from '@/renderer/filter/anime4k';

import { toaster } from '@/components/GlobalToast';

export const UpscaleCharacter = () => {
const source = usePureStore($upscaleSource);
const [isInit, setIsInit] = createSignal<boolean>(false);
let canvasRef!: HTMLCanvasElement;
let requestId: number | null = null;
let gpuResource: GpuResource | null = null;
let renderer: Anime4KRenderer | null = null;
const useType: (PipelineOption | PipelineType)[] = [
{
pipeline: PipelineType.BilateralMean,
params: {
strength: 0.2,
strength2: 1,
},
},
{
pipeline: PipelineType.Dog,
params: {
strength: 2,
},
},
PipelineType.CNNSoftVL,
];

onMount(async () => {
const soruceCanvas = source();
if (!soruceCanvas) {
return;
}
try {
gpuResource = await createGpuDevice(canvasRef);

renderer = await createRendererWithPipelines(soruceCanvas, canvasRef, {
...gpuResource,
pipelines: useType,
});
setIsInit(true);
} catch (e) {
console.error(e);
toaster.error({
title: '無法初始化 WebGPU',
description: '請確保您的 Webview 版本支援 WebGPU。',
});
}
});

onCleanup(() => {
if (requestId) {
cancelAnimationFrame(requestId);
}
if (gpuResource) {
gpuResource.context.unconfigure();
}
});

createEffect(async () => {
if (!isInit()) {
return;
}
const sourceCanvas = source();
function frame() {
if (renderer && sourceCanvas) {
renderer.updateFrameTexture();
renderer.render();
}
}
function loop() {
frame();
requestId = requestAnimationFrame(loop);
}
requestId = requestAnimationFrame(loop);
});

return <canvas ref={canvasRef} width="300" height="340" />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useStore } from '@nanostores/solid';

import {
$enableExperimentalUpscale,
setEnableExperimentalUpscale,
} from '@/store/settingDialog';

import { HStack } from 'styled-system/jsx/hstack';
import { Text } from '@/components/ui/text';
import { Switch, type ChangeDetails } from '@/components/ui/switch';
import { SettingTooltip } from '@/components/dialog/SettingDialog/SettingTooltip';

export const UpscaleSwitch = () => {
const enableExperimentalUpscale = useStore($enableExperimentalUpscale);

function handleChange(details: ChangeDetails) {
setEnableExperimentalUpscale(details.checked);
}

return (
<Switch
checked={enableExperimentalUpscale()}
onCheckedChange={handleChange}
>
<HStack gap="1">
<Text>實驗性高清預覽</Text>
<SettingTooltip tooltip="新增按鈕顯示高清化的角色預覽,開啟時顯示高清版(Anime4K)的圖片在旁邊,此功能可能造成極大的效能影響,請確認有足夠的電腦資源再使用" />
</HStack>
</Switch>
);
};
4 changes: 4 additions & 0 deletions src/components/dialog/SettingDialog/RenderSetting/index.tsx
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ import { DefaultCharacterRenderingSwitch } from './DefaultCharacterRenderingSwit
import { ShowItemGenderSwitch } from './ShowItemGenderSwitch';
import { ShowItemDyeableSwitch } from './ShowItemDyeableSwitch';
import { ItemEffectPreview } from './ItemEffectPreview';
import { UpscaleSwitch } from './UpscaleSwitch';
import { SettingTooltip } from '@/components/dialog/SettingDialog/SettingTooltip';

export const RenderSetting = () => {
@@ -26,6 +27,9 @@ export const RenderSetting = () => {
<ShowItemDyeableSwitch />
<ItemEffectPreview />
</HStack>
<HStack gap="8">
<UpscaleSwitch />
</HStack>
</Stack>
);
};
30 changes: 30 additions & 0 deletions src/renderer/filter/anime4k/fullscreenTexturedQuad.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
struct VertexOutput {
@builtin(position) Position : vec4<f32>,
@location(0) fragUV : vec2<f32>,
}

@vertex
fn vert_main(@builtin(vertex_index) VertexIndex : u32) -> VertexOutput {
const pos = array(
vec2( 1.0, 1.0),
vec2( 1.0, -1.0),
vec2(-1.0, -1.0),
vec2( 1.0, 1.0),
vec2(-1.0, -1.0),
vec2(-1.0, 1.0),
);

const uv = array(
vec2(1.0, 0.0),
vec2(1.0, 1.0),
vec2(0.0, 1.0),
vec2(1.0, 0.0),
vec2(0.0, 1.0),
vec2(0.0, 0.0),
);

var output : VertexOutput;
output.Position = vec4(pos[VertexIndex], 0.0, 1.0);
output.fragUV = uv[VertexIndex];
return output;
}
311 changes: 311 additions & 0 deletions src/renderer/filter/anime4k/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
import type { Anime4KPipeline } from 'anime4k-webgpu';
import fullscreenTexturedQuadVert from './fullscreenTexturedQuad.wgsl';
import sampleExternalTextureFrag from './sampleExternalTexture.wgsl';

type Anime4kPipelineConstructor = new (desc: {
device: GPUDevice;
inputTexture: GPUTexture;
nativeDimensions?: { width: number; height: number };
targetDimensions?: { width: number; height: number };
}) => Anime4KPipeline;

export enum PipelineType {
/**
* deblur, must set param: strength
*
* @param strength Deblur Strength (0.1 - 15.0)
*/
Dog = 'Dog',
/**
* denoise, must set param: strength, strength2
*
* @param strength Itensity Sigma (0.1 - 2.0)
* @param strength2 Spatial Sigma (1 - 15)
*/
BilateralMean = 'BilateralMean',

/** restore */
CNNM = 'CNNM',
/** restore */
CNNSoftM = 'CNNSoftM',
/** restore */
CNNSoftVL = 'CNNSoftVL',
/** restore */
CNNVL = 'CNNVL',
/** restore */
CNNUL = 'CNNUL',
/** restore */
GANUUL = 'GANUUL',

/** upscale */
CNNx2M = 'CNNx2M',
/** upscale */
CNNx2VL = 'CNNx2VL',
/** upscale */
DenoiseCNNx2VL = 'DenoiseCNNx2VL',
/** upscale */
CNNx2UL = 'CNNx2UL',
/** upscale */
GANx3L = 'GANx3L',
/** upscale */
GANx4UUL = 'GANx4UUL',

ClampHighlights = 'ClampHighlights',
DepthToSpace = 'DepthToSpace',

/* anime4k preset @see https://github.com/bloc97/Anime4K/blob/master/md/GLSL_Instructions_Advanced.md */
/** Restore -> Upscale -> Upscale */
ModeA = 'ModeA',
/** Restore_Soft -> Upscale -> Upscale */
ModeB = 'ModeB',
/** Upscale_Denoise -> Upscale */
ModeC = 'ModeC',
/** Restore -> Upscale -> Restore -> Upscale */
ModeAA = 'ModeAA',
/** Restore_Soft -> Upscale -> Restore_Soft -> Upscale */
ModeBB = 'ModeBB',
/** Upscale_Denoise -> Restore -> Upscale */
ModeCA = 'ModeCA',
}

const PipelineMap: Record<PipelineType, () => Promise<unknown>> = {
[PipelineType.Dog]: () => import('anime4k-webgpu').then((m) => m.DoG),
[PipelineType.BilateralMean]: () =>
import('anime4k-webgpu').then((m) => m.BilateralMean),

[PipelineType.CNNM]: () => import('anime4k-webgpu').then((m) => m.CNNM),
[PipelineType.CNNSoftM]: () =>
import('anime4k-webgpu').then((m) => m.CNNSoftM),
[PipelineType.CNNSoftVL]: () =>
import('anime4k-webgpu').then((m) => m.CNNSoftVL),
[PipelineType.CNNVL]: () => import('anime4k-webgpu').then((m) => m.CNNVL),
[PipelineType.CNNUL]: () => import('anime4k-webgpu').then((m) => m.CNNUL),
[PipelineType.GANUUL]: () => import('anime4k-webgpu').then((m) => m.GANUUL),

[PipelineType.CNNx2M]: () => import('anime4k-webgpu').then((m) => m.CNNx2M),
[PipelineType.CNNx2VL]: () => import('anime4k-webgpu').then((m) => m.CNNx2VL),
[PipelineType.DenoiseCNNx2VL]: () =>
import('anime4k-webgpu').then((m) => m.DenoiseCNNx2VL),
[PipelineType.CNNx2UL]: () => import('anime4k-webgpu').then((m) => m.CNNx2UL),
[PipelineType.GANx3L]: () => import('anime4k-webgpu').then((m) => m.GANx3L),
[PipelineType.GANx4UUL]: () =>
import('anime4k-webgpu').then((m) => m.GANx4UUL),

[PipelineType.ClampHighlights]: () =>
import('anime4k-webgpu').then((m) => m.ClampHighlights),
[PipelineType.DepthToSpace]: () =>
import('anime4k-webgpu').then((m) => m.DepthToSpace),

[PipelineType.ModeA]: () => import('anime4k-webgpu').then((m) => m.ModeA),
[PipelineType.ModeB]: () => import('anime4k-webgpu').then((m) => m.ModeB),
[PipelineType.ModeC]: () => import('anime4k-webgpu').then((m) => m.ModeC),
[PipelineType.ModeAA]: () => import('anime4k-webgpu').then((m) => m.ModeAA),
[PipelineType.ModeBB]: () => import('anime4k-webgpu').then((m) => m.ModeBB),
[PipelineType.ModeCA]: () => import('anime4k-webgpu').then((m) => m.ModeCA),
};

export type GpuResource = {
device: GPUDevice;
context: GPUCanvasContext;
};

export async function createGpuDevice(canvas: HTMLCanvasElement) {
const adapter = await navigator.gpu.requestAdapter();
if (!adapter) {
throw new Error('WebGPU is not supported');
}
const device = await adapter.requestDevice();
const context = canvas.getContext('webgpu') as GPUCanvasContext;
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device,
format: presentationFormat,
alphaMode: 'premultiplied',
});

return { device, context };
}

export interface PipelineOption {
pipeline: PipelineType;
params: Record<string, number>;
}

export interface CreateRendererOptions {
device: GPUDevice;
context: GPUCanvasContext;
pipelines: (PipelineType | PipelineOption)[];
}

export type Anime4KRenderer = {
render: () => void;
updateFrameTexture: () => void;
};

export async function createRendererWithPipelines(
sourceCanvas: HTMLCanvasElement,
destCanvas: HTMLCanvasElement,
options: CreateRendererOptions,
) {
const { device, context, pipelines } = options;
const { width: WIDTH, height: HEIGHT } = sourceCanvas;

destCanvas.width = WIDTH;
destCanvas.height = HEIGHT;

const mainTexture = device.createTexture({
size: [WIDTH, HEIGHT, 1],
format: 'rgba16float',
usage:
GPUTextureUsage.TEXTURE_BINDING |
GPUTextureUsage.COPY_DST |
GPUTextureUsage.RENDER_ATTACHMENT,
});

const loadPipelines = await Promise.all(
pipelines.map((pipeline) => {
if (typeof pipeline === 'string') {
return PipelineMap[pipeline]();
}
return PipelineMap[pipeline.pipeline]();
}),
);

const pipelineChain: Anime4KPipeline[] = loadPipelines.reduce(
(chain: Anime4KPipeline[], pipeline, index) => {
const inputTexture =
index === 0 ? mainTexture : chain[index - 1].getOutputTexture();
const pipeClass = pipeline as Anime4kPipelineConstructor;
const pipeOption = pipelines[index];
const pipe = new pipeClass({
device,
inputTexture,
nativeDimensions: { width: WIDTH, height: HEIGHT },
targetDimensions: { width: WIDTH, height: HEIGHT },
});
if (typeof pipeOption !== 'string' && pipeOption.params) {
for (const [key, value] of Object.entries(pipeOption.params)) {
pipe.updateParam(key, value);
}
}
chain.push(pipe);
return chain;
},
[] as Anime4KPipeline[],
);

const lastPipeline = pipelineChain[pipelineChain.length - 1];
// const lastTexture = lastPipeline.getOutputTexture();
// destCanvas.width = lastTexture.width;
// destCanvas.height = lastTexture.height;

// render pipeline setups
const renderBindGroupLayout = device.createBindGroupLayout({
label: 'Render Bind Group Layout',
entries: [
{
binding: 1,
visibility: GPUShaderStage.FRAGMENT,
sampler: {},
},
{
binding: 2,
visibility: GPUShaderStage.FRAGMENT,
texture: {},
},
],
});

const renderPipelineLayout = device.createPipelineLayout({
label: 'Render Pipeline Layout',
bindGroupLayouts: [renderBindGroupLayout],
});

const renderPipeline = device.createRenderPipeline({
layout: renderPipelineLayout,
vertex: {
module: device.createShaderModule({
code: fullscreenTexturedQuadVert,
}),
entryPoint: 'vert_main',
},
fragment: {
module: device.createShaderModule({
code: sampleExternalTextureFrag,
}),
entryPoint: 'main',
targets: [
{
format: navigator.gpu.getPreferredCanvasFormat(),
},
],
},
primitive: {
topology: 'triangle-list',
},
});

const sampler = device.createSampler({
magFilter: 'linear',
minFilter: 'linear',
});

const renderBindGroup = device.createBindGroup({
layout: renderBindGroupLayout,
entries: [
{
binding: 1,
resource: sampler,
},
{
binding: 2,
resource: lastPipeline.getOutputTexture().createView(),
},
],
});

function updateFrameTexture() {
device.queue.copyExternalImageToTexture(
{ source: sourceCanvas },
{ texture: mainTexture },
[WIDTH, HEIGHT],
);
}
function render() {
let textureView: GPUTextureView | undefined;
try {
textureView = context.getCurrentTexture().createView();
} catch (e) {
console.info('render canceled');
}
if (!textureView) {
return;
}
const commandEncoder = device.createCommandEncoder();
for (const pipe of pipelineChain) {
pipe.pass(commandEncoder);
}
const passEncoder = commandEncoder.beginRenderPass({
colorAttachments: [
{
view: textureView,
clearValue: {
r: 0.0,
g: 0.0,
b: 0.0,
a: 1.0,
},
loadOp: 'clear',
storeOp: 'store',
},
],
});
passEncoder.setPipeline(renderPipeline);
passEncoder.setBindGroup(0, renderBindGroup);
passEncoder.draw(6);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
}

return { render, updateFrameTexture };
}
7 changes: 7 additions & 0 deletions src/renderer/filter/anime4k/sampleExternalTexture.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@group(0) @binding(1) var mySampler: sampler;
@group(0) @binding(2) var myTexture: texture_2d<f32>;

@fragment
fn main(@location(0) fragUV : vec2f) -> @location(0) vec4f {
return textureSampleBaseClampToEdge(myTexture, mySampler, fragUV);
}
10 changes: 10 additions & 0 deletions src/store/expirement/upscale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { atom } from 'nanostores';

export const $upscaleSource = atom<HTMLCanvasElement | null>(null);

export function setUpscaleSource(canvas: HTMLCanvasElement | null) {
$upscaleSource.set(canvas);
}
export function resetUpscaleSource() {
$upscaleSource.set(null);
}
9 changes: 9 additions & 0 deletions src/store/settingDialog.ts
Original file line number Diff line number Diff line change
@@ -39,6 +39,7 @@ export interface AppSetting extends Record<string, unknown> {
defaultCharacterRendering: boolean;
showItemGender: boolean;
showItemDyeable: boolean;
enableExperimentalUpscale: boolean;
}

const DEFAULT_SETTING: AppSetting = {
@@ -50,6 +51,7 @@ const DEFAULT_SETTING: AppSetting = {
defaultCharacterRendering: false,
showItemGender: true,
showItemDyeable: true,
enableExperimentalUpscale: false,
};

export const $appSetting = deepMap<AppSetting>(DEFAULT_SETTING);
@@ -81,6 +83,10 @@ export const $showItemDyeable = computed(
$appSetting,
(setting) => setting.showItemDyeable,
);
export const $enableExperimentalUpscale = computed(
$appSetting,
(setting) => setting.enableExperimentalUpscale,
);

/* action */
export async function initializeSavedSetting() {
@@ -152,3 +158,6 @@ export function setShowItemGender(value: boolean) {
export function setShowItemDyeable(value: boolean) {
$appSetting.setKey('showItemDyeable', value);
}
export function setEnableExperimentalUpscale(value: boolean) {
$appSetting.setKey('enableExperimentalUpscale', value);
}
2 changes: 2 additions & 0 deletions src/store/trigger.ts
Original file line number Diff line number Diff line change
@@ -12,6 +12,8 @@ export const $sceneSelectionOpen = atom<boolean>(false);

export const $showPreviousCharacter = atom<boolean>(false);

export const $showUpscaledCharacter = atom<boolean>(false);

export const $confirmDialogOpen = atom<boolean>(false);

export const $settingDialogOpen = atom<boolean>(false);

0 comments on commit 6a37532

Please sign in to comment.