I am currently converting some code from using OpenGL to WGPU, and I have hit a problem I can't seem to resolve. In my original code, I set the scissor rectangle before clearing the render area, as I want to keep most of my framebuffer intact and only re-render the area that has been modified.
However, from what I can find, the clearing operation seems to be only available as a loading operation in WGPU, when creating the render pass. So when I set the scissor rectangle, which is done on the render pass itself, the clearing has already been performed during creation, clearing the entire framebuffer.
Am I missing something or is this currently not possible on WGPU?
Regards,
Gustav
let mut render_pass = encoder.begin_render_pass(
&wgpu::RenderPassDescriptor {
label: Some("Render Pass"),
color_attachments: &[Some(
wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(
wgpu::Color {
r: 0.04,
g: 0.08,
b: 0.08,
a: 1.0,
}
),
store: true,
},
}
)],
depth_stencil_attachment: None,
}
);
render_pass.set_scissor_rect(
render_area.x,
render_area.y,
render_area.width(),
render_area.height()
);
In wgpu/WebGPU, updating only part of the framebuffer means that the previous framebuffer needs to be loaded:
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: true,
},
})],
depth_stencil_attachment: None,
});
and then the scissor_rect can be set to draw to the specified area.
Related
I'm trying to follow along the "hello compute" example from wgpu on Windows 10 (with some minor modifications, mainly gutting the shader so it does basically no actual computing), but when I read the buffer at the end, it's always zeroed out.
This is the shader I'm trying to run, it compiles fine and I think it's correct
[[block]]
struct Numbers
{
data: [[stride(4)]] array<u32>;
};
[[group(0), binding(0)]]
var<storage, read_write> numbers: Numbers;
[[stage(compute), workgroup_size(1)]]
fn main()
{
numbers.data[0] = numbers.data[0] + u32(1);
numbers.data[1] = numbers.data[1] + u32(1);
numbers.data[2] = numbers.data[2] + u32(1);
}
As for the wgpu code, it follows the tutorial quite closely:
I get the instance, device, and queue
let instance = Instance::new(Backends::PRIMARY);
let adapter = block_on(instance
.request_adapter(&RequestAdapterOptions
{
power_preference: PowerPreference::default(),
compatible_surface: None,
}))
.unwrap();
let (device, queue) = block_on(adapter
.request_device(&Default::default(), None))
.unwrap();
Compile the shader and make a pipeline:
let shader = device.create_shader_module(&ShaderModuleDescriptor
{
label: Some("shader"),
source: ShaderSource::Wgsl(shader_src.into()),
});
let pipeline = device.create_compute_pipeline(&ComputePipelineDescriptor
{
label: None,
layout: None,
module: &shader,
entry_point: "main",
});
Make the staging and storage buffer. The dbg!(size) prints 12, which should be correct for a 3-length array for 4-byte u32s.
let buffer = [1u32, 2, 3];
let size = std::mem::size_of_val(&buffer) as u64;
dbg!(size);
let staging_buffer = device.create_buffer(&BufferDescriptor
{
label: None,
size: size,
usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let storage_buffer = device.create_buffer_init(&BufferInitDescriptor
{
label: Some("storage buffer"),
contents: cast_slice(&buffer),
usage: BufferUsages::STORAGE
| BufferUsages::COPY_DST
| BufferUsages::COPY_SRC,
});
set up the bind group:
let bg_layout = pipeline.get_bind_group_layout(0);
let bind_group = device.create_bind_group(&BindGroupDescriptor
{
label: None,
layout: &bg_layout,
entries: &[BindGroupEntry
{
binding: 0,
resource: storage_buffer.as_entire_binding(),
}]
});
Get the encoder and create the compute pass. The copy_buffer_to_buffer should copy the storage buffer to the staging buffer so I can read it at the end.
let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor
{
label: None,
});
{
let mut cpass = encoder.begin_compute_pass(&ComputePassDescriptor
{
label: None
});
cpass.set_pipeline(&pipeline);
cpass.set_bind_group(0, &bind_group, &[]);
cpass.dispatch(1, 1, 1);
}
encoder.copy_buffer_to_buffer(
&storage_buffer, 0,
&staging_buffer, 0,
size);
queue.submit(Some(encoder.finish()));
And then submit the compute pass and block for the result:
let buf_slice = staging_buffer.slice(..);
let buf_future = buf_slice.map_async(MapMode::Read);
device.poll(Maintain::Wait);
if let Ok(()) = block_on(buf_future)
{
let data = buf_slice.get_mapped_range();
let result = cast_slice::<u8, u32>(&data).to_vec();
drop(data);
staging_buffer.unmap();
println!("{:?}", result);
}
else
{
println!("error");
}
The error case isn't reached, and the program terminates with no errors, but the result is always printed [0, 0 ,0], when it should be [2, 3, 4].
What am I doing wrong?
The program works fine when I'm running it on my discrete graphics card, but wgpu is bugged on my integrated Intel HD Graphics 630, which is why the program appeared not to work.
It seems that whenever I focus on a TextField (that sits inside a SliverPersistentHeader) SliverList+SliverPersistentHeader scrolls down. I have created some mockups of what I mean below:
So in this mockup, the user starts off at the first layout, scrolls up to continue viewing the lsit and then when they click on the TextField, the whole thing shifts down. Any way to stop that?
I have also attached my basic Scaffold code for your perusal:
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Consts.coMainBackground,
resizeToAvoidBottomInset: false,
body: CustomScrollView(
slivers: <Widget>[
_sliverAppBar(),
_makeHeader(),
BlocBuilder<AllPersonsBloc, AllPersonsState>(
builder: (context, state) {
if (state is AllPersonsLoading) {
return _buildLoading();
} else if (state is AllPersonsLoaded) {
return _sliverList(context, state.persons);
} else if (state is AllPersonsError) {
return _buildErrorMessage(state.message);
} else {
return _buildErrorMessage('Unknown error!');
}
},
),
],
),
);
}
the _makeHeader creates the SliverPersistentHeader and the rest I think should make sense based on names.
Your help would greatly appreciated :)
Thanks!
Got it...
return SliverAppBar(
automaticallyImplyLeading: false,
backgroundColor: Consts.coForestGreenBackground,
expandedHeight: 207,
titleSpacing: 0,
elevation: 0,
floating: false,
pinned: false,
snap: false,
flexibleSpace: FlexibleSpaceBar(
Note that the item is not pinned/floated/snapped. Then its important that the input (TextField in this case) has a scrollPadding (top) of 0.
Your scaffold will ALSO need an appbar. So technically you have an appbar and a SliverAppBar but the SliverAppBar is just to wrap the flexibleSpace.
Or rather, zero after any padding on the element it self. In my case its 40 since the TextField has a top padding of 30 and another 10 from the element that contains it etc.
I have a small issue that and I’m not able to figure out what’s wrong, maybe someone can help.
It seems player.anims.play(‘walk’) is not working I get an error which reads “Cannot read property ‘frame’ of undefined”.
It seems I get an empty array of frames.
How I load my spreadsheet in preload():
this.load.spritesheet('player', 'assets/characters.png', { frameWidth: 32, frameHeight: 48 })
How my class looks :
export default class Player extends Phaser.Physics.Arcade.Sprite {
constructor(scene: Phaser.Scene, x: number, y: number) {
super(scene, x, y, 'player')
const player = scene.physics.add.sprite(x, y, 'player')
player.setCollideWorldBounds(true).setInteractive()
scene.anims.create({
key: 'walk',
frames: scene.anims.generateFrameNames('player', { start: 1, end: 3 }),
repeat: -1
})
player.anims.play('walk')
}
}
in create():
this.player = new Player(this, 10, 10)
Found the solution I’m using Phaser 3.21.0 and it seems that inside this.load.spritesheet I have to pass endFrame inside config object
I have added a common Surface and a Scrollview (actually a FlexScrollView) to a SequentialLayout and then added the layout to my View as the following code shows:
function _createCameraView() {
var layoutViews = [];
this.cameraView = new View({
size: [undefined, this.options.footerSize]
});
var layout = new SequentialLayout({
size: [undefined, this.options.footerSize],
direction: Utility.Direction.Y
});
var backgroundSurface = new Surface({
size: [undefined, 120],
content: 'some content',
classes : ['photo-guide-content'],
properties: {
backgroundColor: 'transparent',
zIndex: 4
}
});
// detail list
var detailsList = new FlexScrollView({
layout: ListLayout,
direction: Utility.Direction.X, // set direction to horizontal
paginated: true,
//autoPipeEvents: true,
useContainer: true,
container : {
size: [undefined, 60],
properties : {
'z-index' : 5,
}
}
});
layoutViews.push(backgroundSurface);
layoutViews.push(detailsList);
layout.sequenceFrom(layoutViews);
this.cameraView.add(alignModifier).add(layout);
}
Then I've added a RenderController on another view and I'm using it to display my cameraView like this:
function _selectCamera() {
if (!this.cameraView) {
_createCameraView.call(this);
}
this.footerRender.hide(this.mapView);
this.footerRender.show(this.cameraView);
}
I know I don't need to call the hide method from the render on the function above, just the show method and it should swap the views with a smooth opacity transition, which is defined by default. Although when using this Sequential Layout I'm not getting this effect. I know it has to do with this layout because I've tried to switch the layout to a FlexibleLayout and a GridLayout, and it works fine with these two. So, my question is, why doesn't work with the SequentialLayout? I've also noticed it has a method called setOutputFunction(outputFunction), is there a way to use it so the layout uses the opacity returned from the RenderController or how can it be fixed some other way?
Try adding a getSize method to your view.
Something like this:
this.cameraView.getSize = function() {
return [undefined, undefined];
};
We are using extjs 3.4. The purpose is to replace a component in a Ext.Window. Even if
there is no error when we remove the old and add the new component, when trying doLayout() the error
Uncaught TypeError: Cannot read property 'offsetWidth' of undefined
occurs.
The code that creates the window is:
function createWindowConf(id, winconf, items) {
var conf = {
id: id,
title: winconf.title,
iconCls: winconf.icon,
x : winconf.xpos,
y : winconf.ypos,
width : parseInt(winconf.xsize),
height : parseInt(winconf.ysize),
layout : winconf.layout, //'border',
border : false,
resizable : winconf.resizable,
manager: windows,
shadow: false,
closable: true,
items: items
};
var win = new Ext.Window(conf);
win.render(desktopEl);
return win;
};
The items are a Ext.grid.GridPanel and a Ext.form.FormPanel.
According to user's current selection in grip (component in position 0) the old form should be removed and a new should be added (component in position 1).
The code that creates the form is:
var theConfig = {
id: config.yid,
bodyStyle: 'padding:5px 5px 0',
width: 370,
maxWidth: 370,//not resizable
minWidth: 370,
layout: 'form',
margins: '0 0 0',
region: 'east',
split: true,
colapsible : true,
trackResetOnLoad: true,
autoScroll: true,
fieldDefaults: {
msgTarget: 'side',
labelWidth: 75
},
items: [],
buttons: [scopeDetails.updateButton, scopeDetails.submitButton]
};
this.detailsForm = new Ext.form.FormPanel(theConfig);
and the items added afterwards.
The code that updates (adds/removes) components from the window is:
this.updateWin = function(moduleForRemoveId, form, idWin) {
var win = this.getWindow(idWin);
if (win != null) {
win.remove(moduleForRemoveId);
win.add(form);
win.doLayout();
}
}
and produces the error in win.doLayout().
By removing all components and add the add the new ones:
win.removeAll();
win.add(this.grid);
win.add(this.form);
win.doLayout();
we have the same error.
Any suggestion will be really helpful since more than 3 days spent for that error
I'm pretty sure the error comes from something else rather than the component remove/add.
I created a simple testcase which defines a window with a form and a grid, and a button which replaces everything with a panel. And it works.
Your code is incomplete. You should provide a full example.
Have you tried to debug it yourself? Enable "pause on uncaught exceptions" on your Chrome script debugger the reproduce the bug. You will get a stack trace of what happened, giving you hints on what is wrong. Of course include ext-all-debug.